PHP permite registrar manejadores globales para errores y excepciones que no han sido capturados localmente. Con set_error_handler() interceptas los errores de nivel (E_WARNING, E_NOTICE, etc.) y con set_exception_handler() capturas las excepciones que llegan sin capturar hasta el nivel más alto. Juntos forman la red de seguridad de cualquier aplicación PHP.
set_error_handler(): capturar warnings y notices
<?php
set_error_handler(function (
int $errno,
string $errstr,
string $errfile,
int $errline,
): bool {
// Respetar el operador de silencio @
if (!(error_reporting() & $errno)) {
return false;
}
$nivel = match ($errno) {
E_WARNING, E_USER_WARNING => 'WARNING',
E_NOTICE, E_USER_NOTICE => 'NOTICE',
E_ERROR, E_USER_ERROR => 'ERROR',
default => 'UNKNOWN',
};
$mensaje = "[{$nivel}] {$errstr} en {$errfile}:{$errline}";
error_log($mensaje);
// Devolver true significa que el manejador de PHP no procesa este error
return true;
});
// Ahora los warnings van a nuestro manejador
trigger_error('Esto es un aviso personalizado', E_USER_WARNING);
?>
Convertir errores en excepciones con ErrorException
Una práctica habitual es convertir todos los errores PHP en ErrorException para tratarlos con el mismo mecanismo de try/catch:
<?php
set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline): bool {
if (!(error_reporting() & $errno)) {
return false;
}
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});
try {
// trigger_error convierte el warning en ErrorException
$resultado = file_get_contents('/ruta/que/no/existe.txt');
} catch (ErrorException $e) {
echo 'Error capturado: ' . $e->getMessage();
}
?>
set_exception_handler(): capturar excepciones no capturadas
<?php
set_exception_handler(function (Throwable $e): void {
$tipo = get_class($e);
$mensaje = $e->getMessage();
$archivo = $e->getFile();
$linea = $e->getLine();
// En producción: loggear y mostrar página amigable
error_log("[EXCEPCIÓN] {$tipo}: {$mensaje} en {$archivo}:{$linea}");
if (PHP_SAPI === 'cli') {
echo "Error fatal: {$mensaje}n";
} else {
http_response_code(500);
echo '<h1>Error interno del servidor</h1>';
}
});
// Esta excepción no capturada llega al handler
throw new RuntimeException('Algo salió muy mal');
?>
register_shutdown_function(): capturar errores fatales
Los errores fatales (E_ERROR) no pueden capturarse con set_error_handler(), pero sí se puede reaccionar a ellos con una función registrada al cierre del script:
<?php
register_shutdown_function(function (): void {
$error = error_get_last();
if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
$mensaje = "[ERROR FATAL] {$error['message']} en {$error['file']}:{$error['line']}";
error_log($mensaje);
if (!headers_sent()) {
http_response_code(500);
header('Content-Type: application/json');
}
echo json_encode(['error' => 'Error interno del servidor']);
}
});
?>
Configurar error_reporting correctamente
<?php
// Desarrollo: mostrar todo
error_reporting(E_ALL);
ini_set('display_errors', '1');
// Producción: loggear todo, no mostrar nada en pantalla
error_reporting(E_ALL);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', '/var/log/php/app.log');
// Comprobar si un nivel está activo
if (error_reporting() & E_WARNING) {
echo 'Los warnings están activos';
}
?>
La documentación de set_error_handler(), la de set_exception_handler() y la de ErrorException detallan los niveles de error disponibles, el comportamiento con el operador @ y las limitaciones de cada función.
