Cómo resolver la excepción de PHP 7: Warning: DOMDocument::loadHTML(): Tag 'figure, nav, section' invalid

Cómo resolver la excepción de PHP 7: Warning: DOMDocument::loadHTML(): Tag 'figure, nav, section' invalid

DOM Document es una clase PHP que representa un documento HTML o XML completo y sirve como raíz del árbol del documento. Se utiliza para crear o cargar HTML o XML fácilmente y modificarlo a tu gusto, buscar elementos, etc. En los últimos días, necesitaba recuperar la fuente (URL) de las imágenes cargadas dentro de un documento HTML y decidí hacerlo fácilmente con la clase mencionada y DomXPath fácilmente en PHP. Desafortunadamente, mientras cargaba HTML 5 muy básico y estándar, descubrí el siguiente problema que, curiosamente, aunque esto desencadena una excepción, el mensaje habla explícitamente de una advertencia:

Warning: DOMDocument::loadHTML(): Tag XXXXXX invalid in Entity

¿Por qué aparece esta excepción?

La ejecución del siguiente código PHP activará la mencionada "Advertencia" que estropea tu código:

<?php

// Ejemplo de HTML que desencadena la excepcion:
$html = <<<'HTML'
    <!DOCTYPE html>
    <html>
        <head>
            <title>Testing</title>
        </head>
        <body id='foo'>
            <h1>Hello World</h1>
            <figure class="image">
                <img src="https://ourcodeworld.com/public-media/articles/cookielessdomain-5fa35742d669f.png" />
                <figcaption>Caption</figcaption>
            </figure>
        </body>
    </html>
HTML;

$domDocument = new \DOMDocument();

$domDocument->loadHTML($html);

$xpath = new \DOMXPath($domDocument);

foreach($xpath->query("//img/@src") as $item){
    echo "<br> Image: ". basename($item->value);
}

Luego, en el navegador aparecerán los siguientes errores:

Warning: DOMDocument::loadHTML(): Tag figure invalid in Entity, line: 7 in \demo.php on line 27

Warning: DOMDocument::loadHTML(): Tag figcaption invalid in Entity, line: 9 in \demo.php on line 27

Image: cookielessdomain-5fa35742d669f.png

Descubrí este error al intentar buscar las URL de la imagen dentro de una estructura HTML para obtener el valor del atributo src de cada una de ellas. El error está por sí solo en la clase DOMDocument. En nuestro HTML, tenemos 2 entidades HTML5 ( <figure><figcaption>) que no son reconocidas por el antiguo analizador DOMDocument de PHP.

Solución

Hay 2 posibles soluciones para este problema:

A. Ignorar las advertencias

Lo primero que puedes intentar es simplemente ignorar estas advertencias suprimiéndolas, forzando a libxml a manejar los errores internamente con libxml_use_internal_errors (puedes recuperarlos con código), y luego limpiarlos como se especifica en el siguiente ejemplo:

// 1. Crear documento
$domDocument = new \DOMDocument();

// 2. Manejar errores internamente
libxml_use_internal_errors(true);

// 3. Carga tu HTML 5
$domDocument->loadHTML($html);

// 4. Haz lo que tengas que hacer sin la advertencia ...

// 5. Limpiar errores
libxml_clear_errors();

Como el error en sí es causado por la biblioteca libxml subyacente, en teoría, tu código completo (o al menos una gran parte de el) funcionará de todos modos si ignoramos la excepción mencionada. Si tu código aún funciona como se esperaba, entonces no necesitas probar la segunda solución posible. Si necesitas conocer los errores o advertencias, puedes obtenerlos y hacer lo que necesites con ellos también:

// 1. Crear documento
$domDocument = new \DOMDocument();

// 2. Manejar errores internamente
libxml_use_internal_errors(true);

// 3. Carga tu HTML 5
$domDocument->loadHTML($html);

// 4. Haz lo que tengas que hacer sin la advertencia ...
$xpath = new \DOMXPath($domDocument);

foreach($xpath->query("//img/@src") as $item){
    echo "<br> Image: ". basename($item->value);
}  

// 5. Limpiar errores
$errors = libxml_get_errors();

// 6. Si necesita conocer los errores o advertencias
foreach ($errors as $error)
{
    /* @var $error LibXMLError */
    /*
    cada variable $error contiene un objeto LibXMLError con las siguientes propiedades
    array(
        'level' => 2,
        'code' => 801,
        'column' => 28,
        'message' => 'Tag figcaption invalid',
        'file' => '',
        'line' => 10,
    )
    */
}

Sin embargo, si por alguna razón, después de ignorar las advertencias, tu código no se comporta como se esperaba, entonces puedes probar nuestra segunda solución posible para este problema.

B. Utiliza otro parser (DomCrawler)

Al final de este problema, debes lograr algo con el DOM, probablemente buscando dentro de él y no modificándolo, por lo que es muy probable que tu problema se resuelva si confías en un parser DOM compatible con HTML5 y ahí es cuando entra la biblioteca DomCrawler de Symfony. El componente DomCrawler facilita la navegación DOM para documentos HTML y XML.

Para trabajar con esta biblioteca, procede con la instalación usando Composer:

composer require symfony/dom-crawler

Para obtener más información sobre esta biblioteca, visita el repositorio oficial de Github aquí o el sitio web oficial aquí .

Después de la instalación, deberías poder incluir la biblioteca en tu código. El siguiente fragmento muestra básicamente lo mismo que hicimos en el código original con DOMXPath que buscaba las imágenes en el HTML 5 proporcionado:

<?php

require 'vendor/autoload.php';

use Symfony\Component\DomCrawler\Crawler;

// Un documento HTML de ejemplo:
$html = <<<'HTML'
    <!DOCTYPE html>
    <html>
        <head>
            <title>Testing</title>
        </head>
        <body id='foo'>
            <h1>Hello World</h1>
            <figure class="image">
                <img src="https://ourcodeworld.com/public-media/articles/cookielessdomain-5fa35742d669f.png" />
                <figcaption>Caption</figcaption>
            </figure>
        </body>
    </html>
HTML;

// 1. Crea una instancia del rastreador con nuestro HTML
$crawler = new Crawler($html);

// 2. Busque las imágenes y el atributo src usando el filtro XPath y guárdelos en una matriz
$images = $crawler->filterXPath('//img/@src')->each(function (Crawler $node, $i) {
    return $node;
});

// 3. Iterar las imágenes encontradas y obtener lo que queremos
foreach($images as $image){
    echo "Image: "$image->text();
}

Que debería generar en el navegador algo como:

Image: https://ourcodeworld.com/public-media/articles/cookielessdomain-5fa35742d669f.png

Como puede ver, nuestro requisito se resolvió y no hubo advertencias de entidades desconocidas mientras se cargaba el HTML en el parser.

Que te diviertas ❤️!

Esto podria interesarte

Conviertete en un programador más sociable