set_error_handler() y set_exception_handler() en PHP: capturar errores globalmente

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.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP