¿Puedo intentar / atrapar una advertencia?

358

Necesito detectar algunas advertencias que se lanzan desde algunas funciones nativas de php y luego manejarlas.

Específicamente:

array dns_get_record  ( string $hostname  [, int $type= DNS_ANY  [, array &$authns  [, array &$addtl  ]]] )

Lanza una advertencia cuando falla la consulta DNS.

try/ catchno funciona porque una advertencia no es una excepción.

Ahora tengo 2 opciones:

  1. set_error_handler parece excesivo porque tengo que usarlo para filtrar cada advertencia en la página (¿es esto cierto?);

  2. Ajuste el informe / visualización de errores para que estas advertencias no se repitan en la pantalla, luego verifique el valor de retorno; si es así false, no se encuentran registros para el nombre de host.

¿Cuál es la mejor práctica aquí?

usuario121196
fuente
1
stackoverflow.com/questions/136899/… es una buena discusión sobre cosas como esta.
Mez
hubo una respuesta a continuación que se eliminó? ya sea por el dueño o por alguien?
user121196
ver también: stackoverflow.com/questions/1087365
dreftymac
@ usuario121196: Sí. Por el dueño.
ligereza corre en órbita

Respuestas:

373

Establecer y restaurar el controlador de errores

Una posibilidad es configurar su propio controlador de errores antes de la llamada y restaurar el controlador de errores anterior más adelante con restore_error_handler().

set_error_handler(function() { /* ignore errors */ });
dns_get_record();
restore_error_handler();

Podría aprovechar esta idea y escribir un controlador de errores reutilizable que registre los errores por usted.

set_error_handler([$logger, 'onSilencedError']);
dns_get_record();
restore_error_handler();

Convertir errores en excepciones

Puede usar set_error_handler()y la ErrorExceptionclase para convertir todos los errores de php en excepciones.

set_error_handler(function($errno, $errstr, $errfile, $errline, $errcontext) {
    // error was suppressed with the @-operator
    if (0 === error_reporting()) {
        return false;
    }

    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});

try {
    dns_get_record();
} catch (ErrorException $e) {
    // ...
}

Lo importante a tener en cuenta al usar su propio controlador de errores es que omitirá la error_reportingconfiguración y pasará todos los errores (avisos, advertencias, etc.) a su controlador de errores. Puede establecer un segundo argumento set_error_handler()para definir qué tipos de error desea recibir o acceder a la configuración actual utilizando ... = error_reporting()el controlador de errores.

Suprimiendo la advertencia

Otra posibilidad es suprimir la llamada con el operador @ y verificar el valor de retorno de dns_get_record()después. Pero desaconsejaría esto, ya que los errores / advertencias se activan para ser manejados, no para ser suprimidos.

Philippe Gerber
fuente
3
¿Es aconsejable configurar mi propio controlador de errores justo antes de la llamada a la función, y luego restore_error_handler cuando se realiza la comprobación de errores?
user121196
2
¿será seguro para subprocesos si hay muchas solicitudes simultáneas, y cada solicitud hace 1.set_error_handler (). 2.doit 3.restore_error_handler?
user121196
44
Gracias; esto ayuda. (Y dicen que PHP no es un desastre.)
Aaron Miller
2
+1 para evitar el uso de @ para suprimir errores. E_WARNING es en realidad un error no fatal. En general, siempre debe tratar de manejar los errores de manera adecuada. Si su aplicación requiere el uso de set_error_handler, hágalo. Por lo general, es aconsejable registrar errores y deshabilitar su visualización en un entorno de producción. Al revisar los registros, puede ver dónde hacer cambios en su entorno de desarrollo. Demasiadas instancias en las que he visto @ fopen / @ unlink y me pregunto por qué el desarrollador no realizó verificaciones para evitar los errores o manejar el error usando set_error_handler.
fyrye
55
Una nota sobre cómo convertir las advertencias en excepciones: una advertencia no anulará su aplicación, ¡una excepción no detectada lo hará!
Álvaro González,
149

La solución que realmente funciona es establecer un controlador de errores simple con E_WARNINGparámetros, de esta manera:

set_error_handler("warning_handler", E_WARNING);
dns_get_record(...)
restore_error_handler();

function warning_handler($errno, $errstr) { 
// do something
}
Robert
fuente
44
También callablese puede usar aquí anónimo en lugar de una cadena con declaración de función
vp_arth
Gracias, pero ¿cómo puedo eliminar el controlador de errores después del bloqueo crítico?
Yevgeniy Afanasyev
3
¡Excelente! Solo trow new \Exception($errstr, $errno);dentro de la warning_handlerfunción. Gracias.
Vladimir Vukanac
¡Esta es la mejor respuesta aquí!
lewis4u
28

Tenga cuidado con el @operador : mientras suprime las advertencias, también elimina los errores fatales. Pasé mucho tiempo depurando un problema en un sistema donde alguien había escrito @mysql_query( '...' )y el problema era que el soporte de mysql no estaba cargado en PHP y arrojó un error fatal silencioso. Será seguro para aquellas cosas que son parte del núcleo de PHP, pero úselo con cuidado.

bob@mypc:~$ php -a
Interactive shell

php > echo @something(); // this will just silently die...

No hay más resultados: ¡buena suerte para depurar esto!

bob@mypc:~$ php -a
Interactive shell

php > echo something(); // lets try it again but don't suppress the error
PHP Fatal error:  Call to undefined function something() in php shell code on line 1
PHP Stack trace:
PHP   1. {main}() php shell code:0
bob@mypc:~$ 

Esta vez podemos ver por qué falló.

GuruBob
fuente
5

Quería intentar / detectar una advertencia, pero al mismo tiempo mantener el registro habitual de advertencia / error (por ejemplo, en /var/log/apache2/error.log); por lo cual el manejador tiene que regresar false. Sin embargo, dado que la instrucción "lanzar nuevo ..." básicamente interrumpe la ejecución, uno tiene que hacer el truco de "envolver en función", que también se analiza en:

¿Hay una forma estática de lanzar una excepción en php

O, en resumen:

  function throwErrorException($errstr = null,$code = null, $errno = null, $errfile = null, $errline = null) {
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
  }
  function warning_handler($errno, $errstr, $errfile, $errline, array $errcontext) {
    return false && throwErrorException($errstr, 0, $errno, $errfile, $errline);
    # error_log("AAA"); # will never run after throw
    /* Do execute PHP internal error handler */
    # return false; # will never run after throw
  }
  ...
  set_error_handler('warning_handler', E_WARNING);
  ...
  try {
    mkdir($path, 0777, true);
  } catch (Exception $e) {
    echo $e->getMessage();
    // ...
  }

EDITAR: después de una inspección más cercana, resulta que no funciona: " return false && throwErrorException ..." básicamente, no arrojará la excepción, y simplemente ingresará al registro de errores; eliminar la " false &&" parte, como en " return throwErrorException ...", hará que el lanzamiento de excepción funcione, pero luego no se registrará en el error_log ... Sin embargo, aún lo mantendré publicado, ya que no he visto este comportamiento documentado en otro lugar.

sdaau
fuente
4

Probablemente debería intentar deshacerse de la advertencia por completo, pero si eso no es posible, puede anteponer la llamada con @ (es decir, @dns_get_record (...)) y luego usar cualquier información que pueda averiguar si ocurrió la advertencia o no.

rpjohnst
fuente
4

Normalmente, nunca debe usar @ a menos que esta sea la única solución. En ese caso específico, la función dns_check_record debe usarse primero para saber si existe el registro.

Florynth
fuente
3

La combinación de estas líneas de código en torno a una file_get_contents()llamada a una URL externa me ayudó a manejar advertencias como " no se pudo abrir la transmisión: la conexión agotó el tiempo de espera " mucho mejor:

set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line, array $err_context)
{
    throw new ErrorException( $err_msg, 0, $err_severity, $err_file, $err_line );
}, E_WARNING);
try {
    $iResult = file_get_contents($sUrl);
} catch (Exception $e) {
    $this->sErrorMsg = $e->getMessage();
}
restore_error_handler();

Esta solución también funciona dentro del contexto del objeto. Podrías usarlo en una función:

public function myContentGetter($sUrl)
{
  ... code above ...
  return $iResult;
}
Bugfighter
fuente
2

Si dns_get_record()falla, debería regresar FALSE, para que pueda suprimir la advertencia con @y luego verificar el valor de retorno.

Ámbar
fuente
0

intente comprobar si devuelve algún valor booleano, simplemente puede ponerlo como condición. Encontré esto con el oci_execute (...) que estaba devolviendo alguna violación con mis claves únicas.

ex.
oci_parse($res, "[oracle pl/sql]");
if(oci_execute){
...do something
}
gborjal
fuente
0

Carpeta Estructura

index.php //Script File
logs //Folder for log Every warning and Errors
CustomException.php //Custom exception File

CustomException.php

/**
* Custom error handler
*/
function handleError($code, $description, $file = null, $line = null, $context = null) {
    $displayErrors = ini_get("display_errors");;
    $displayErrors = strtolower($displayErrors);
    if (error_reporting() === 0 || $displayErrors === "on") {
        return false;
    }
    list($error, $log) = mapErrorCode($code);
    $data = array(
        'timestamp' => date("Y-m-d H:i:s:u", time()),
        'level' => $log,
        'code' => $code,
        'type' => $error,
        'description' => $description,
        'file' => $file,
        'line' => $line,
        'context' => $context,
        'path' => $file,
        'message' => $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']'
    );
    $data = array_map('htmlentities',$data);
    return fileLog(json_encode($data));
}

/**
* This method is used to write data in file
* @param mixed $logData
* @param string $fileName
* @return boolean
*/
function fileLog($logData, $fileName = ERROR_LOG_FILE) {
    $fh = fopen($fileName, 'a+');
    if (is_array($logData)) {
        $logData = print_r($logData, 1);
    }
    $status = fwrite($fh, $logData . "\n");
    fclose($fh);
//    $file = file_get_contents($filename);
//    $content = '[' . $file .']';
//    file_put_contents($content); 
    return ($status) ? true : false;
}

/**
* Map an error code into an Error word, and log location.
*
* @param int $code Error code to map
* @return array Array of error word, and log location.
*/
function mapErrorCode($code) {
    $error = $log = null;
    switch ($code) {
        case E_PARSE:
        case E_ERROR:
        case E_CORE_ERROR:
        case E_COMPILE_ERROR:
        case E_USER_ERROR:
            $error = 'Fatal Error';
            $log = LOG_ERR;
            break;
        case E_WARNING:
        case E_USER_WARNING:
        case E_COMPILE_WARNING:
        case E_RECOVERABLE_ERROR:
            $error = 'Warning';
            $log = LOG_WARNING;
            break;
        case E_NOTICE:
        case E_USER_NOTICE:
            $error = 'Notice';
            $log = LOG_NOTICE;
            break;
        case E_STRICT:
            $error = 'Strict';
            $log = LOG_NOTICE;
            break;
        case E_DEPRECATED:
        case E_USER_DEPRECATED:
            $error = 'Deprecated';
            $log = LOG_NOTICE;
            break;
        default :
            break;
    }
    return array($error, $log);
}
//calling custom error handler
set_error_handler("handleError");

simplemente incluya el archivo anterior en su archivo de secuencia de comandos como este

index.php

error_reporting(E_ALL);
ini_set('display_errors', 'off');
define('ERROR_LOG_FILE', 'logs/app_errors.log');

include_once 'CustomException.php';
echo $a; // here undefined variable warning will be logged into logs/app_errors.log
Juned Ansari
fuente
-2

Solo recomendaría usar @ para suprimir las advertencias cuando se trata de una operación directa (por ejemplo, $ prop = @ ($ high / ($ width - $ depth)); para omitir la división por cero advertencias). Sin embargo, en la mayoría de los casos, es mejor manejarlo.

tanovellino
fuente
2
Esta es una vez que definitivamente no desea usar @: tiene control sobre la operación y puede verificar si es una división por cero o no antes de hacerlo.
Eborbob