Deshabilite las advertencias al cargar HTML no bien formado por DomDocument (PHP)

79

Necesito analizar algunos archivos HTML, sin embargo, no están bien formados y PHP imprime advertencias en. Quiero evitar tal comportamiento de depuración / advertencia mediante programación. Por favor avise. ¡Gracias!

Código:

// create a DOM document and load the HTML data
$xmlDoc = new DomDocument;
// this dumps out the warnings
$xmlDoc->loadHTML($fetchResult);

Esta:

@$xmlDoc->loadHTML($fetchResult)

puedo suprimir las advertencias, pero ¿cómo puedo capturar esas advertencias mediante programación?

Viet
fuente
Pruebe esta solución - parece ser mucho más fácil - stackoverflow.com/questions/6090667/…
Marcin
Convertir una entrada pésima en una salida adecuada es lo que paga las facturas;) La opción de recuperación está en el manual . es solo un booleano. Puede simplemente llamar $dom->saveHTML()para ver de qué tipo si el documento libxml está tratando de hacer con su $htmlentrada, generalmente está bastante cerca / bien.
Wrikken

Respuestas:

13

Puede instalar un controlador de errores temporal con set_error_handler

class ErrorTrap {
  protected $callback;
  protected $errors = array();
  function __construct($callback) {
    $this->callback = $callback;
  }
  function call() {
    $result = null;
    set_error_handler(array($this, 'onError'));
    try {
      $result = call_user_func_array($this->callback, func_get_args());
    } catch (Exception $ex) {
      restore_error_handler();        
      throw $ex;
    }
    restore_error_handler();
    return $result;
  }
  function onError($errno, $errstr, $errfile, $errline) {
    $this->errors[] = array($errno, $errstr, $errfile, $errline);
  }
  function ok() {
    return count($this->errors) === 0;
  }
  function errors() {
    return $this->errors;
  }
}

Uso:

// create a DOM document and load the HTML data
$xmlDoc = new DomDocument();
$caller = new ErrorTrap(array($xmlDoc, 'loadHTML'));
// this doesn't dump out any warnings
$caller->call($fetchResult);
if (!$caller->ok()) {
  var_dump($caller->errors());
}
troelskn
fuente
10
Parece una exageración para la situación. Tenga en cuenta las funciones libxml2 de PHP.
thomasrutter
Buen punto, Thomas. No sabía acerca de estas funciones cuando escribí esta respuesta. Si no me equivoco, hace lo mismo internamente por cierto.
troelskn
1
Tiene el mismo efecto en este caso sí, aunque se hace a un nivel diferente: con la solución anterior, los errores de PHP se generan pero se suprimen pero con el mío, no se convierten en errores de PHP. Personalmente, creo que si hacer algo implica suprimir errores de PHP a través de @ o set_error_handler (), entonces es la forma incorrecta de hacerlo. Aunque esa es sólo mi opinión. Tenga en cuenta que los errores y las excepciones de PHP son algo completamente diferente: usar try {} catch () {} está bien.
thomasrutter
2
Creo que he visto algunos informes de errores que sugieren que se libxml_use_internal_errorsconecta al controlador de errores de php.
troelskn
Espero que la gente pase de esta respuesta a las mejores respuestas a continuación.
thomasrutter
222

Llamada

libxml_use_internal_errors(true);

antes de procesar con con $xmlDoc->loadHTML()

Esto le dice a libxml2 que no envíe errores y advertencias a PHP. Luego, para verificar si hay errores y manejarlos usted mismo, puede consultar libxml_get_last_error () y / o libxml_get_errors () cuando esté listo.

thomasrutter
fuente
1
Mucho más fácil que agregar 20 líneas de código como lo hace la respuesta aceptada. ¡Gracias!
Brian Klug
94

Para ocultar las advertencias, hay que dar instrucciones especiales a las libxmlque se utilizan internamente para realizar el análisis:

libxml_use_internal_errors(true);
$dom->loadHTML($html);
libxml_clear_errors();

El libxml_use_internal_errors(true)indica que usted mismo manejará los errores y las advertencias y no quiere que arruinen la salida de su script.

Este no es el mismo que el @operador. Las advertencias se recopilan detrás de escena y luego puede recuperarlas libxml_get_errors()en caso de que desee realizar el registro o devolver la lista de problemas a la persona que llama.

Ya sea que esté usando las advertencias recopiladas o no, siempre debe despejar la cola llamando libxml_clear_errors().

Preservando el estado

Si tiene otro código que lo use libxml, puede ser útil asegurarse de que su código no altere el estado global del manejo de errores; para esto, puede usar el valor de retorno de libxml_use_internal_errors()para guardar el estado anterior.

// modify state
$libxml_previous_state = libxml_use_internal_errors(true);
// parse
$dom->loadHTML($html);
// handle errors
libxml_clear_errors();
// restore
libxml_use_internal_errors($libxml_previous_state);
Jack
fuente
2
@Greeso: Se establece en el valor anterior . Eso se hace por el concepto de que podría haber sido configurado para algún otro código globalmente diferente FALSEy configurarlo FALSEdespués destruiría esa configuración. Al utilizar el valor de retorno anterior, $libxml_previous_statese evitan esos posibles efectos secundarios porque la configuración original se ha restaurado independientemente de las necesidades de este lugar. El libxml_use_internal_errors()entorno es global, por lo que vale la pena tener cuidado.
hakre
Si ya hay errores libxml pendientes, ¿no los comerá esto?
cHao
@cHao, ¿no es razonable suponer que está comenzando con una pizarra en blanco? :)
Ja͢ck
@ Ja͢ck: No. Si se llamó a algo anteriormente libxml_use_internal_errors(true), entonces puede estar esperando para manejar cualquier error que haya surgido.
cHao
23

Establecer las opciones "LIBXML_NOWARNING" y "LIBXML_NOERROR" también funciona perfectamente bien:

$dom->loadHTML($html, LIBXML_NOWARNING | LIBXML_NOERROR);
Joshua Ott
fuente