¿Cómo guardar HTML de DOMDocument sin envoltorio HTML?

Soy la función a continuación, me cuesta publicar el DOMDocument sin que añada los envoltorios XML, HTML, body y p antes del resultado del contenido. La solución sugerida:

$postarray['post_content'] = $d->saveXML($d->getElementsByTagName('p')->item(0)); 

Solo funciona cuando el contenido no tiene elementos de nivel de bloque dentro de él. Sin embargo, cuando lo hace, como en el ejemplo siguiente con el elemento h1, la salida resultante de saveXML se trunca en …

Si te gusta

Me han señalado esta publicación como una solución posible, pero no puedo entender cómo implementarla en esta solución (ver los bashs comentados a continuación).

¿Alguna sugerencia?

 function rseo_decorate_keyword($postarray) { global $post; $keyword = "Jasmine Tea" $content = "If you like 

jasmine tea

you will really like it with Jasmine Tea flavors. This is the last ocurrence of the phrase jasmine tea within the content. If there are other instances of the keyword jasmine tea within the text what happens to jasmine tea." $d = new DOMDocument(); @$d->loadHTML($content); $x = new DOMXpath($d); $count = $x->evaluate("count(//text()[contains(translate(., 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghjiklmnopqrstuvwxyz'), '$keyword') and (ancestor::b or ancestor::strong)])"); if ($count > 0) return $postarray; $nodes = $x->query("//text()[contains(translate(., 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghjiklmnopqrstuvwxyz'), '$keyword') and not(ancestor::h1) and not(ancestor::h2) and not(ancestor::h3) and not(ancestor::h4) and not(ancestor::h5) and not(ancestor::h6) and not(ancestor::b) and not(ancestor::strong)]"); if ($nodes && $nodes->length) { $node = $nodes->item(0); // Split just before the keyword $keynode = $node->splitText(strpos($node->textContent, $keyword)); // Split after the keyword $node->nextSibling->splitText(strlen($keyword)); // Replace keyword with keyword $replacement = $d->createElement('strong', $keynode->textContent); $keynode->parentNode->replaceChild($replacement, $keynode); } $postarray['post_content'] = $d->saveXML($d->getElementsByTagName('p')->item(0)); // $postarray['post_content'] = $d->saveXML($d->getElementsByTagName('body')->item(1)); // $postarray['post_content'] = $d->saveXML($d->getElementsByTagName('body')->childNodes); return $postarray; }

Todas estas respuestas están ahora mal , porque a partir de PHP 5.4 y Libxml 2.6 loadHTML ahora tiene un parámetro $option que instruye a Libxml sobre cómo debe analizar el contenido.

Por lo tanto, si cargamos el HTML con estas opciones

 $html->loadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); 

al saveHTML() no habrá doctype , no y ningún .

LIBXML_HTML_NOIMPLIED desactiva la adición automática de elementos html / body implicados. LIBXML_HTML_NODEFDTD impide que se LIBXML_HTML_NODEFDTD un doctype predeterminado cuando no se encuentra uno.

La documentación completa sobre los parámetros de Libxml está aquí

(Tenga en cuenta que los documentos loadHTML dicen que se necesita Libxml 2.6, pero LIBXML_HTML_NODEFDTD solo está disponible en Libxml 2.7.8 y LIBXML_HTML_NOIMPLIED está disponible en Libxml 2.7.7)

Simplemente elimine los nodos directamente después de cargar el documento con loadHTML ():

 # remove removeChild($doc->doctype); # remove  $doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild); 

Use saveXML() y pase el elemento documentElement como argumento.

 $innerHTML = ''; foreach ($document->getElementsByTagName('p')->item(0)->childNodes as $child) { $innerHTML .= $document->saveXML($child); } echo $innerHTML; 

http://php.net/domdocument.savexml

use DOMDocumentFragment

 $html = 'what you want'; $doc = new DomDocument(); $fragment = $doc->createDocumentFragment(); $fragment->appendXML($html); $doc->appendChild($fragment); echo $doc->saveHTML(); 

Un buen truco es usar loadXML y luego saveHTML . Las tags html y body se insertan en la etapa de load , no en la etapa de save .

 $dom = new DOMDocument; $dom->loadXML('

My DOMDocument contents are here

'); echo $dom->saveHTML();

NB que esto es un poco raro y deberías usar la respuesta de Jonás si puedes hacer que funcione.

Estoy un poco tarde en el club, pero no quería compartir un método que descubrí. En primer lugar, tengo las versiones correctas para loadHTML () para aceptar estas bonitas opciones, pero LIBXML_HTML_NOIMPLIED no funcionaba en mi sistema. También los usuarios informan problemas con el analizador (por ejemplo aquí y aquí ).

La solución que creé en realidad es bastante simple.

El HTML que se va a cargar se coloca en un elemento

por lo que tiene un contenedor que contiene todos los nodos que se cargarán.

Luego, este elemento contenedor se elimina del documento (pero el elemento DOME todavía existe).

Luego se eliminan todos los hijos directos del documento. Esto incluye cualquier etiqueta , y LIBXML_HTML_NOIMPLIED (efectivamente la opción LIBXML_HTML_NOIMPLIED ) así como también la statement (efectivamente LIBXML_HTML_NODEFDTD ).

A continuación, todos los elementos secundarios directos del contenedor se agregan al documento nuevamente y se puede generar.

 $str = '

Lorem ipsum dolor sit amet.

Nunc vel vehicula ante.

'; $doc = new DOMDocument(); $doc->loadHTML("
$str
"); $container = $doc->getElementsByTagName('div')->item(0); $container = $container->parentNode->removeChild($container); while ($doc->firstChild) { $doc->removeChild($doc->firstChild); } while ($container->firstChild ) { $doc->appendChild($container->firstChild); } $htmlFragment = $doc->saveHTML();

XPath funciona como siempre, solo tenga cuidado de que haya varios elementos de documento ahora, de modo que no haya un único nodo raíz:

 $xpath = new DOMXPath($doc); foreach ($xpath->query('/p') as $element) { # ^- note the single slash "/" # ... each of the two 

element


  • PHP 5.4.36-1 + deb.sury.org ~ precise + 2 (cli) (built: Dec 21 2014 20:28:53)

Use esta función

 $layout = preg_replace('~<(?:!DOCTYPE|/?(?:html|head|body))[^>]*>\s*~i', '', $layout); 

El problema con la respuesta principal es que LIBXML_HTML_NOIMPLIED es inestable .

Puede reordenar elementos (particularmente, mover la etiqueta de cierre del elemento superior a la parte inferior del documento), agregar tags p aleatorias y tal vez una variedad de otros problemas [1] . Puede eliminar las tags html y body para usted, pero a costa de un comportamiento inestable. En producción, eso es una bandera roja. En breve:

No use LIBXML_HTML_NOIMPLIED . En cambio, use substr .


Piénsalo. Las longitudes de y son fijas y en ambos extremos del documento: sus tamaños nunca cambian y tampoco lo hacen sus posiciones. Esto nos permite usar substr para eliminarlos:

 $dom = new domDocument; $dom->loadHTML($html, LIBXML_HTML_NODEFDTD); echo substr($dom->saveHTML(), 12, -15); // the star of this operation 

( ¡SIN EMBARGO, NO ES LA SOLUCIÓN FINAL! Vea a continuación la respuesta completa , siga leyendo para el contexto)

Cortamos 12 desde el inicio del documento porque = 12 caracteres ( <<>>+html+body = 4 + 4 + 4), y retrocedemos y cortamos 15 del final porque \n = 15 caracteres ( \n+//+<<>>+body+html = 1 + 2 + 4 + 4 + 4)

Tenga en cuenta que todavía uso LIBXML_HTML_NODEFDTD omita el !DOCTYPE de ser incluido. En primer lugar, esto simplifica la eliminación de substr de las tags HTML / BODY. En segundo lugar, no eliminamos el doctype con substr porque no sabemos si el ‘ default doctype ‘ siempre tendrá algo de longitud fija. Pero, lo que es más importante, LIBXML_HTML_NODEFDTD impide que el analizador DOM aplique un tipo de documento no HTML5 al documento, lo que al menos impide que el analizador trate los elementos que no reconoce como texto suelto.

Sabemos a LIBXML_HTML_NODEFDTD cierta que las tags HTML / BODY son de longitudes y posiciones fijas, y sabemos que las constantes como LIBXML_HTML_NODEFDTD nunca se eliminan sin algún tipo de aviso de LIBXML_HTML_NODEFDTD , por lo que el método anterior debería extenderse en el futuro, PERO


… la única advertencia es que la implementación DOM podría cambiar la forma en que las tags HTML / BODY se colocan dentro del documento, por ejemplo, eliminar la nueva línea al final del documento, agregar espacios entre las tags o agregar nuevas líneas.

Esto se puede remediar buscando las posiciones de las tags de apertura y cierre para el body , y usando esas compensaciones en cuanto a nuestras longitudes para recortar. Utilizamos strpos y strrpos para encontrar los desplazamientos desde la parte frontal y posterior, respectivamente:

 $dom = new domDocument; $dom->loadHTML($html, LIBXML_HTML_NODEFDTD); $trim_off_front = strpos($dom->saveHTML(),'') + 6; // PositionOf + 6 = Cutoff offset after '' // 6 = Length of '' $trim_off_end = (strrpos($dom->saveHTML(),'')) - strlen($dom->saveHTML()); // ^ PositionOf - LengthOfDocument = Relative-negative cutoff offset before '' echo substr($dom->saveHTML(), $trim_off_front, $trim_off_end); 

Para terminar, una repetición de la respuesta final, a prueba de futuro :

 $dom = new domDocument; $dom->loadHTML($html, LIBXML_HTML_NODEFDTD); $trim_off_front = strpos($dom->saveHTML(),'') + 6; $trim_off_end = (strrpos($dom->saveHTML(),'')) - strlen($dom->saveHTML()); echo substr($dom->saveHTML(), $trim_off_front, $trim_off_end); 

Sin doctype, sin etiqueta html, sin etiqueta de cuerpo. Solo podemos esperar que el analizador DOM reciba pronto una nueva capa de pintura y podamos eliminar más directamente estas tags no deseadas.

Ninguna de las otras soluciones en el momento de escribir estas líneas (junio de 2012) fue capaz de satisfacer completamente mis necesidades, así que escribí una que maneja los siguientes casos:

  • Acepta contenido de texto sin formato que no tiene tags, así como contenido HTML.
  • No tags (incluidas las tags , , , y

    )

  • Deja todo envuelto en

    solo.

  • Deja el texto en blanco solo

Así que aquí hay una solución que soluciona esos problemas:

 class DOMDocumentWorkaround { /** * Convert a string which may have HTML components into a DOMDocument instance. * * @param string $html - The HTML text to turn into a string. * @return \DOMDocument - A DOMDocument created from the given html. */ public static function getDomDocumentFromHtml($html) { $domDocument = new DOMDocument(); // Wrap the HTML in 
tags because loadXML expects everything to be within some kind of tag. // LIBXML_NOERROR and LIBXML_NOWARNING mean this will fail silently and return an empty DOMDocument if it fails. $domDocument->loadXML('
' . $html . '
', LIBXML_NOERROR | LIBXML_NOWARNING); return $domDocument; } /** * Convert a DOMDocument back into an HTML string, which is reasonably close to what we started with. * * @param \DOMDocument $domDocument * @return string - The resulting HTML string */ public static function getHtmlFromDomDocument($domDocument) { // Convert the DOMDocument back to a string. $xml = $domDocument->saveXML(); // Strip out the XML declaration, if one exists $xmlDeclaration = "\n"; if (substr($xml, 0, strlen($xmlDeclaration)) == $xmlDeclaration) { $xml = substr($xml, strlen($xmlDeclaration)); } // If the original HTML was empty, loadXML collapses our
into
. Remove it. if ($xml == "
\n") { $xml = ''; } else { // Remove the opening
tag we previously added, if it exists. $openDivTag = "
"; if (substr($xml, 0, strlen($openDivTag)) == $openDivTag) { $xml = substr($xml, strlen($openDivTag)); } // Remove the closing
tag we previously added, if it exists. $closeDivTag = "
\n"; $closeChunk = substr($xml, -strlen($closeDivTag)); if ($closeChunk == $closeDivTag) { $xml = substr($xml, 0, -strlen($closeDivTag)); } } return $xml; } }

También escribí algunas pruebas que vivirían en esa misma clase:

 public static function testHtmlToDomConversions($content) { // test that converting the $content to a DOMDocument and back does not change the HTML if ($content !== self::getHtmlFromDomDocument(self::getDomDocumentFromHtml($content))) { echo "Failed\n"; } else { echo "Succeeded\n"; } } public static function testAll() { self::testHtmlToDomConversions('

Here is some sample text

'); self::testHtmlToDomConversions('
Lots of
nested
divs
'); self::testHtmlToDomConversions('Normal Text'); self::testHtmlToDomConversions(''); //empty }

Puedes verificar que funcione para ti. DomDocumentWorkaround::testAll() devuelve esto:

  Succeeded Succeeded Succeeded Succeeded 

De acuerdo, encontré una solución más elegante, pero es simplemente tediosa:

 $d = new DOMDocument(); @$d->loadHTML($yourcontent); ... // do your manipulation, processing, etc of it blah blah blah ... // then to save, do this $x = new DOMXPath($d); $everything = $x->query("body/*"); // retrieves all elements inside body tag if ($everything->length > 0) { // check if it retrieved anything in there $output = ''; foreach ($everything as $thing) { $output .= $d->saveXML($thing); } echo $output; // voila, no more annoying html wrappers or body tag } 

Muy bien, con suerte, esto no omite nada y ayuda a alguien?

Es 2017, y para esta pregunta de 2011 no me gusta ninguna de las respuestas. Un montón de expresiones regulares, grandes clases, loadXML, etc …

Solución fácil que resuelve los problemas conocidos:

 $dom = new DOMDocument(); $dom->loadHTML( ''.mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8').'' , LIBXML_HTML_NODEFDTD); $html = substr(trim($dom->saveHTML()),12,-14); 

Fácil, simple, sólido, rápido. Este código funcionará con respecto a las tags HTML y la encoding como:

 $html = '

äöü

ß

';

Si alguien encuentra un error, dígale que lo usaré yo mismo.

Editar , Otras opciones válidas que funcionan sin errores (muy similares a las ya dadas):

 @$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8')); $saved_dom = trim($dom->saveHTML()); $start_dom = stripos($saved_dom,'')+6; $html = substr($saved_dom,$start_dom,strripos($saved_dom,'') - $start_dom ); 

Puedes agregar tu cuerpo para evitar cualquier cosa extraña en el fururo.

Thirt opción:

  $mock = new DOMDocument; $body = $dom->getElementsByTagName('body')->item(0); foreach ($body->childNodes as $child){ $mock->appendChild($mock->importNode($child, true)); } $html = trim($mock->saveHTML()); 

Agregar la etiqueta activará el comportamiento de fijación de DOMDocument . Lo bueno es que no necesita agregar esa etiqueta en absoluto. Si desea utilizar una encoding de su elección, simplemente páselo como un argumento de constructor.

http://php.net/manual/en/domdocument.construct.php

 $doc = new DOMDocument('1.0', 'UTF-8'); $node = $doc->createElement('div', 'Hello World'); $doc->appendChild($node); echo $doc->saveHTML(); 

Salida

 
Hello World

Gracias a @Bart

También tenía este requisito y me gustó la solución publicada por Alex anteriormente. Sin embargo, hay un par de problemas: si el elemento contiene más de un elemento secundario, el documento resultante solo contendrá el primer elemento secundario de , no todos. Además, necesitaba el stripping para manejar las cosas condicionalmente, solo cuando tenía un documento con los encabezados HTML. Así que lo refiné de la siguiente manera. En lugar de eliminar , lo transformé en un

y eliminé la statement XML y .

 function strip_html_headings($html_doc) { if (is_null($html_doc)) { // might be better to issue an exception, but we silently return return; } // remove firstChild) && $html_doc->firstChild->nodeType == XML_DOCUMENT_TYPE_NODE) { $html_doc->removeChild($html_doc->firstChild); } if (!is_null($html_doc->firstChild) && strtolower($html_doc->firstChild->tagName) == 'html' && !is_null($html_doc->firstChild->firstChild) && strtolower($html_doc->firstChild->firstChild->tagName) == 'body') { // we have 'html/body' - replace both nodes with a single "div" $div_node = $html_doc->createElement('div'); // copy all the child nodes of 'body' to 'div' foreach ($html_doc->firstChild->firstChild->childNodes as $child) { // deep copies each child node, with attributes $child = $html_doc->importNode($child, true); // adds node to 'div'' $div_node->appendChild($child); } // replace 'html/body' with 'div' $html_doc->removeChild($html_doc->firstChild); $html_doc->appendChild($div_node); } } 

Al igual que otros miembros, primero me deleité con la simplicidad y el increíble poder de la respuesta de @Alessandro Vendruscolo. La capacidad de simplemente pasar algunas constantes marcadas al constructor parecía demasiado buena para ser cierta. Para mí fue. Tengo las versiones correctas tanto de LibXML como de PHP, sin embargo, sin importar lo que aún agregue la etiqueta HTML a la estructura de nodos del objeto Document.

Mi solución funcionó mucho mejor que usar el …

 $html->loadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); 

Banderas o ….

 # remove removeChild($doc->firstChild); # remove  $doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild); 

Eliminación de nodo, que se vuelve desordenada sin un orden estructurado en el DOM. De nuevo, los fragmentos de código no tienen forma de predeterminar la estructura DOM.

Comencé este viaje deseando una forma simple de hacer un recorrido DOM como lo hace JQuery o, al menos de alguna manera, que tenía un conjunto de datos estructurados, ya sea individualmente enlazado, doblemente enlazado o atravesado por un nodo en árbol. No me importó cuánto tiempo podría analizar una cadena como lo hace HTML y también tener el sorprendente poder de las propiedades de la clase de entidad nodo para usar en el camino.

Hasta ahora DOMDocument Object me ha dejado con muchas ganas … Al igual que con muchos otros progtwigdores, parece … Sé que he visto mucha frustración en esta pregunta, así que desde FINALMENTE …. (después de aproximadamente 30 horas de prueba y falla tipo prueba) He encontrado una manera de obtenerlo todo. Espero que esto ayude a alguien…

En primer lugar, soy cínico de TODO … jaja …

Me hubiera ido toda la vida antes de acordar con alguien que de todos modos se necesita una clase de terceros en este caso de uso. Mucho era y no soy fan de usar una estructura de clase de terceros, sin embargo, me topé con un gran analizador. (alrededor de 30 veces en Google antes de ceder, así que no te sientas solo si lo evitaste porque parecía cojo de manera no oficial …)

Si está utilizando fragmentos de código y necesita el código, limpio y no afectado por el analizador de ninguna manera, sin utilizar tags adicionales, utilice PHPParser simple .

Es increíble y se parece mucho a JQuery. No siempre me impresionó, pero esta clase utiliza muchas herramientas buenas y hasta ahora no he tenido errores de análisis. Soy un gran fan de poder hacer lo que hace esta clase.

Puede encontrar sus archivos para descargar aquí , sus instrucciones de inicio aquí y su API aquí . Recomiendo usar esta clase con sus métodos simples que pueden hacer un .find(".className") la misma manera que se usaría un método de búsqueda JQuery o incluso métodos conocidos como getElementByTagName() o getElementById()

Cuando guarda un árbol de nodos en esta clase, no agrega nada. Simplemente puede decir $doc->save(); y produce todo el árbol en una cadena sin ningún problema.

Ahora usaré este analizador para todos los proyectos de ancho de banda no limitado en el futuro.

Tengo PHP 5.3 y las respuestas aquí no funcionaron para mí.

$doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild); reemplacé todo el documento con solo el primer hijo, tenía muchos párrafos y solo se guardaba el primero, pero la solución me dio un buen punto de partida para escribir algo sin regex Dejé algunos comentarios y estoy seguro de que esto se puede mejorar, pero si alguien tiene el mismo problema que yo, puede ser un buen punto de partida.

 function extractDOMContent($doc){ # remove removeChild($doc->doctype); // lets get all children inside the body tag foreach ($doc->firstChild->firstChild->childNodes as $k => $v) { if($k !== 0){ // don't store the first element since that one will be used to replace the html tag $doc->appendChild( clone($v) ); // appending element to the root so we can remove the first element and still have all the others } } // replace the body tag with the first children $doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild); return $doc; } 

Entonces podríamos usarlo así:

 $doc = new DOMDocument(); $doc->encoding = 'UTF-8'; $doc->loadHTML('

Some html here

And more html

and some html

'); $doc = extractDOMContent($doc);

Tenga en cuenta que appendChild acepta un DOMNode por lo que no es necesario crear nuevos elementos, solo podemos reutilizar los existentes que implementan DOMNode , como DOMElement Esto puede ser importante para mantener el código “sano” al manipular múltiples documentos HTML / XML.

Encontré este tema para encontrar una manera de eliminar el contenedor HTML. Utilizando LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD funciona muy bien, pero tengo un problema con utf-8. Después de mucho esfuerzo encontré una solución. Lo publico a continuación porque cualquiera tiene el mismo problema.

El problema causado por

El problema:

 $dom = new DOMDocument(); $dom->loadHTML('' . $document, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); $dom->saveHTML(); 

Solución 1:

 $dom->loadHTML(mb_convert_encoding($document, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); $dom->saveHTML($dom->documentElement)); 

Solución 2:

 $dom->loadHTML($document, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); utf8_decode($dom->saveHTML($dom->documentElement)); 

Si la solución de indicadores respondida por Alessandro Vendruscolo no funciona, puede intentar esto:

 $dom = new DOMDocument(); $dom->loadHTML($content); //do your stuff.. $finalHtml = ''; $bodyTag = $dom->documentElement->getElementsByTagName('body')->item(0); foreach ($bodyTag->childNodes as $rootLevelTag) { $finalHtml .= $dom->saveHTML($rootLevelTag); } echo $finalHtml; 

$bodyTag contendrá su código HTML procesado completo sin todas las envolturas HTML, a excepción de la etiqueta , que es la raíz de su contenido. Luego puede usar una expresión regular o una función de recorte para eliminarla de la cadena final (después de saveHTML ) o, como en el caso anterior, iterar sobre todos sus hijos, guardar su contenido en una variable temporal $finalHtml y devolverlo (qué creo estar más seguro).

Me encontré con este problema también.

Desafortunadamente, no me sentí cómoda al usar ninguna de las soluciones provistas en este hilo, así que fui a verificar una que me satisficiera.

Esto es lo que inventé y funciona sin problemas:

 $domxpath = new \DOMXPath($domDocument); /** @var \DOMNodeList $subset */ $subset = $domxpath->query('descendant-or-self::body/*'); $html = ''; foreach ($subset as $domElement) { /** @var $domElement \DOMElement */ $html .= $domDocument->saveHTML($domElement); } 

En esencia, funciona de manera similar a la mayoría de las soluciones proporcionadas aquí, pero en lugar de hacer trabajo manual usa el selector xpath para seleccionar todos los elementos dentro del cuerpo y concatena su código html.

mi servidor tiene php 5.3 y no se puede actualizar, por lo que esas opciones

 LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD 

no son para mi

Para resolver esto le digo a SaveXML Function que imprima el elemento Body y luego simplemente reemplaza el “cuerpo” con “div”

aquí está mi código, espero que esté ayudando a alguien:

 loadHTML(''.$html); $tabContentDomDoc->encoding = 'UTF-8'; $tabContentDomDocBody = $tabContentDomDoc->getElementsByTagName('body')->item(0); if(is_object($tabContentDomDocBody)){ echo (str_replace("body","div",$tabContentDomDoc->saveXML($tabContentDomDocBody))); } ?> 

el utf-8 es para el apoyo hebreo.

La respuesta de Alex es correcta, pero puede causar el siguiente error en los nodos vacíos:

El argumento 1 pasado a DOMNode :: removeChild () debe ser una instancia de DOMNode

Aquí viene mi pequeño mod:

  $output = ''; $doc = new DOMDocument(); $doc->loadHTML($htmlString); //feed with html here if (isset($doc->firstChild)) { /* remove doctype */ $doc->removeChild($doc->firstChild); /* remove html and body */ if (isset($doc->firstChild->firstChild->firstChild)) { $doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild); $output = trim($doc->saveHTML()); } } return $output; 

Agregar el recorte () también es una buena idea para eliminar espacios en blanco.

Quizás sea demasiado tarde. Pero tal vez alguien (como yo) todavía tenga este problema.
Entonces, nada de lo anterior funcionó para mí. Debido a que $ dom-> loadHTML también cierra las tags abiertas, no solo agrega tags html y body.
Así que agregar un elemento

no me funciona, porque a veces tengo como 3 o 4 divisiones abiertas en el fragmento html.
Mi solución:

1.) Agregue marcador para cortar, luego cargue la pieza html

 $html_piece = "[MARK]".$html_piece."[/MARK]"; $dom->loadHTML($html_piece); 

2.) haz lo que quieras con el documento
3.) guardar html

 $new_html_piece = $dom->saveHTML(); 

4.) antes de devolverlo, quite las tags

del marcador, curiosamente solo aparece en [MARK] pero no en [/ MARK] …!?

 $new_html_piece = preg_replace( "/]*?>(\[MARK\]|\s)*?<\/p>/", "[MARK]" , $new_html_piece ); 

5.) eliminar todo antes y después del marcador

 $pattern_contents = '{\[MARK\](.*?)\[\/MARK\]}is'; if (preg_match($pattern_contents, $new_html_piece, $matches)) { $new_html_piece = $matches[1]; } 

6.) devolverlo

 return $new_html_piece; 

Sería mucho más fácil si LIBXML_HTML_NOIMPLIED funcionara para mí. Es schould, pero no lo es. PHP 5.4.17, libxml Versión 2.7.8.
Me parece realmente extraño, utilizo el analizador HTML DOM y luego, para arreglar esta “cosa” tengo que usar expresiones regulares … El punto era, no usar expresiones regulares;)

For anyone using Drupal, there’s a built in function to do this:

https://api.drupal.org/api/drupal/modules!filter!filter.module/function/filter_dom_serialize/7.x

Code for reference:

 function filter_dom_serialize($dom_document) { $body_node = $dom_document->getElementsByTagName('body')->item(0); $body_content = ''; if ($body_node !== NULL) { foreach ($body_node->getElementsByTagName('script') as $node) { filter_dom_serialize_escape_cdata_element($dom_document, $node); } foreach ($body_node->getElementsByTagName('style') as $node) { filter_dom_serialize_escape_cdata_element($dom_document, $node, '/*', '*/'); } foreach ($body_node->childNodes as $child_node) { $body_content .= $dom_document->saveXML($child_node); } return preg_replace('|<([^> ]*)/>|i', '<$1 />', $body_content); } else { return $body_content; } } 

This library makes it simple to traverse / modify the DOM and also takes care of removing the doctype / html wrappers for you:

https://github.com/sunra/php-simple-html-dom-parser

I am struggling with this on RHEL7 running PHP 5.6.25 and LibXML 2.9. (Old stuff in 2018, I know, but that is Red Hat for you.)

I have found that the much upvoted solution suggested by Alessandro Vendruscolo breaks the HTML by rearranging tags. Es decir:

 

First.

Second.

'

se convierte en:

 

First.

Second.

'

This goes for both the options he suggests you use: LIBXML_HTML_NOIMPLIED and LIBXML_HTML_NODEFDTD .

The solution suggested by Alex goes half way to solve it, but it does not work if has more than one child node.

The solution that works for me is the follwing:

First, to load the DOMDocument, I use:

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

To save the document after massaging the DOMDocument, I use:

 // remove removeChild($doc->doctype); $content = $doc->saveHTML(); // remove  $content = str_replace('', '', $content); $content = str_replace('', '', $content); 

I am the first to agree that this this is not a very elegant solution – but it works.