php - etiquetas - meta tags html seo
loadHTML LIBXML_HTML_NOIMPLIED en un fragmento html genera etiquetas incorrectas (2)
El uso de la marca LIBXML_HTML_NOIMPLIED con un fragmento html genera etiquetas incorrectas:
$str = ''<p>Lorem ipsum dolor sit amet.</p><p>Nunc vel vehicula ante.</p>'';
$doc = new DOMDocument();
$doc->loadHTML($str, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
echo $doc->saveHTML();
Salidas:
<p>Lorem ipsum dolor sit amet.<p>Nunc vel vehicula ante.</p></p>
He encontrado hacks para solucionar esto usando expresiones regulares, pero eso anula el propósito de usar DOM. He probado esto con varias versiones de libxml y php, la última con libxml 2.9.2, php 5.6.7 (Debian Jessy). Cualquier sugerencia apreciada.
La opción LIBXML_HTML_NOIMPLIED
no tiene errores, está mal documentada. Para solucionar el problema, envuelva su cadena de entrada con <html>…</html>
, procese su HTML y luego elimínelo. LibXML requiere un nodo raíz, y está tratando el primer elemento que encuentra como el nodo raíz, eliminando la etiqueta de cierre (ubicada incorrectamente) que encuentra a mitad de camino, y luego genera la etiqueta de cierre del primer elemento que encontró al final de el documento. Es lógico cuando lo ves desde la perspectiva de (Lib) XML.
La reorganización se realiza mediante la opción LIBXML_HTML_NOIMPLIED
que está utilizando. Parece que no es lo suficientemente estable para su caso.
También es posible que desee no usarlo por razones de portabilidad, por ejemplo, tengo un PHP 5.4.36 con Libxml 2.7.8 a la mano que no es compatible con LIBXML_HTML_NOIMPLIED
(Libxml> = 2.7.7) pero más tarde LIBXML_HTML_NODEFDTD
(Libxml> = 2.7.8) opción.
Conozco esta forma de lidiar con eso. Cuando carga el fragmento, lo envuelve en un elemento <div>
:
$doc->loadHTML("<div>$str</div>");
Esto ayuda a guiar DOMDocument en la estructura que desea.
A continuación, puede extraer este contenedor del propio documento:
$container = $doc->getElementsByTagName(''div'')->item(0);
$container = $container->parentNode->removeChild($container);
Y luego retire a todos los hijos del documento:
while ($doc->firstChild) {
$doc->removeChild($doc->firstChild);
}
Ahora el documento está completamente vacío y ahora puedes agregar niños nuevamente. Afortunadamente, existe el elemento contenedor <div>
que eliminamos anteriormente, por lo que podemos agregarlo:
while ($container->firstChild ) {
$doc->appendChild($container->firstChild);
}
El fragmento se puede recuperar con el método saveHTML conocido:
echo $doc->saveHTML();
Lo que da en su escenario:
<p>Lorem ipsum dolor sit amet.</p><p>Nunc vel vehicula ante.</p>
Esta metodología es un poco diferente del material existente aquí en el sitio (vea las referencias que doy a continuación), por lo que el ejemplo a la vez:
$str = ''<p>Lorem ipsum dolor sit amet.</p><p>Nunc vel vehicula ante.</p>'';
$doc = new DOMDocument();
$doc->loadHTML("<div>$str</div>");
$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);
}
echo $doc->saveHTML();
También recomiendo la pregunta de referencia sobre ¿Cómo guardar HTML de DOMDocument sin envoltorio HTML? para una lectura adicional, así como la de inner-html
Referencias
- ¿Cómo guardar HTML de DOMDocument sin envoltorio HTML?
- ¿Cómo obtener innerHTML de DOMNode?