Mejores Prácticas de Manejo de Errores en PHP
Acabas de implementar una nueva funcionalidad y, de repente, tu aplicación PHP lanza un error fatal. Los usuarios ven una página en blanco y tus logs están vacíos. ¿Te suena familiar? Un manejo deficiente de errores es una de las razones más comunes por las que el código PHP se vuelve inmantenible. En este artículo, repasaremos prácticas recomendadas de manejo de errores en PHP que harán tu código más robusto, fácil de depurar y más simple de mantener.
Por qué es importante el manejo de errores en PHP
PHP es indulgente por defecto: a menudo continúa la ejecución después de una advertencia, y los errores pueden ignorarse silenciosamente. Esta flexibilidad es un arma de doble filo. Sin un manejo adecuado, los errores pueden:
- Exponer información sensible a los usuarios (por ejemplo, credenciales de base de datos en trazas de pila).
- Corromper datos o dejar la aplicación en un estado inconsistente.
- Hacer que la depuración sea una pesadilla porque los errores están dispersos o suprimidos.
Un buen manejo de errores asegura que cuando algo sale mal, lo sepas, puedas solucionarlo rápidamente y tus usuarios tengan una experiencia fluida.
1. Establece niveles de reporte de errores apropiados
El primer paso es configurar correctamente el reporte de errores de PHP para tu entorno. En desarrollo, quieres ver todos los errores; en producción, quieres registrarlos pero no mostrarlos.
// Desarrollo
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// Producción
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/path/to/php-error.log');
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);
Usa variables de entorno o archivos de configuración para alternar entre estas configuraciones automáticamente. Nunca dependas de editar manualmente php.ini en servidores de producción.
2. Usa excepciones en lugar de códigos de error
Devolver códigos de error (como false o -1) es un patrón heredado que satura tu código y hace fácil ignorar fallos. Las excepciones te obligan a manejar errores explícitamente y mantienen tu camino feliz limpio.
// Malo: código de error
function getUser($id) {
$user = db_find($id);
if (!$user) {
return false; // el llamador debe verificar
}
return $user;
}
// Bueno: excepción
function getUser($id) {
$user = db_find($id);
if (!$user) {
throw new UserNotFoundException("Usuario $id no encontrado");
}
return $user;
}
Crea clases de excepción personalizadas para diferentes tipos de error. Esto facilita capturar errores específicos y manejarlos apropiadamente.
3. Captura excepciones en el nivel correcto
Un error común es capturar excepciones demasiado pronto o de forma demasiado amplia. Captura excepciones solo cuando realmente puedas hacer algo al respecto: registrarlas, reintentar o mostrar un mensaje amigable para el usuario.
try {
$user = getUser($id);
$order = createOrder($user, $items);
} catch (UserNotFoundException $e) {
// Manejar usuario faltante específicamente
return response('Usuario no encontrado', 404);
} catch (PaymentFailedException $e) {
// Manejar fallo de pago
return response('Pago fallido: ' . $e->getMessage(), 400);
} catch (Throwable $e) {
// Captura general para errores inesperados
log_error($e);
return response('Algo salió mal', 500);
}
Usa Throwable (PHP 7+) para capturar tanto excepciones como errores. Evita bloques catch vacíos: si capturas, haz algo significativo.
4. Registra errores con contexto
El logging es tu mejor amigo para depurar problemas en producción. Pero un mensaje de log como "Ocurrió un error" es inútil. Incluye contexto: ID de usuario, parámetros de la solicitud, traza de pila y marcas de tiempo.
try {
processPayment($order);
} catch (PaymentException $e) {
error_log(sprintf(
"Pago fallido para el pedido %d: %s en %s:%d\nTraza de pila: %s",
$order->id,
$e->getMessage(),
$e->getFile(),
$e->getLine(),
$e->getTraceAsString()
));
throw $e; // relanzar después de registrar
}
Considera usar una biblioteca de logging como Monolog para logs estructurados. Soporta diferentes manejadores (archivo, syslog, Slack) y niveles de log (debug, info, warning, error).
5. Crea un manejador de errores personalizado
El manejador de errores predeterminado de PHP imprime errores en pantalla, lo cual no es adecuado para producción. Un manejador de errores personalizado te permite convertir errores en excepciones, registrarlos o mostrar una página de error amigable.
set_error_handler(function ($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
return false; // respetar la configuración de error_reporting
}
throw new ErrorException($message, 0, $severity, $file, $line);
});
set_exception_handler(function ($e) {
log_error($e);
http_response_code(500);
include 'views/error.php';
});
register_shutdown_function(function () {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
log_error(new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']));
}
});
Esta configuración asegura que todos los errores, incluidos los fatales, se registren y manejen con gracia.
6. Valida la entrada y falla temprano
Muchos errores provienen de entradas inválidas. Valida los datos en los límites de tu aplicación (controladores, endpoints de API) y lanza excepciones inmediatamente si la validación falla. Esto evita que los errores se propaguen profundamente en tu código.
function createUser(array $data) {
if (empty($data['email'])) {
throw new InvalidArgumentException('El correo electrónico es obligatorio');
}
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Formato de correo electrónico inválido');
}
// ... proceder con confianza
}
Usa las funciones de filtrado de PHP o una biblioteca de validación (como Respect\Validation) para mantener la validación consistente.
7. No suprimas errores con @
El operador @ silencia errores, dificultando la depuración. Es tentador usarlo al llamar a funciones que podrían emitir advertencias (como file_get_contents), pero oculta problemas reales. En su lugar, verifica precondiciones o usa try-catch con excepciones.
// Malo
$content = @file_get_contents($url);
// Bueno
if (!is_readable($url)) {
throw new RuntimeException("No se puede leer $url");
}
$content = file_get_contents($url);
Si debes suprimir, hazlo solo para casos bien entendidos y documenta por qué.
8. Usa un middleware centralizado para el manejo de errores
En frameworks como Laravel o Symfony, el manejo de errores a menudo se centraliza en middleware o en un manejador de excepciones. Si estás construyendo el tuyo propio, crea un único punto de entrada que capture todas las excepciones y las convierta en respuestas HTTP.
// En tu controlador frontal (index.php)
try {
$response = $router->dispatch($request);
} catch (HttpException $e) {
$response = new Response($e->getMessage(), $e->getStatusCode());
} catch (Throwable $e) {
log_error($e);
$response = new Response('Error Interno del Servidor', 500);
}
$response->send();
Esto mantiene la lógica de manejo de errores en un solo lugar y asegura consistencia.
Comparación: Enfoques de manejo de errores
| Enfoque | Pros | Contras |
|---|---|---|
| Códigos de error | Simple, sin excepciones | Fácil de ignorar, satura el código |
| Excepciones | Fuerza el manejo, separación limpia | Puede usarse en exceso, sobrecarga de rendimiento |
| Manejador de errores personalizado | Centralizado, captura todos los errores | Requiere configuración, puede enmascarar errores si está mal configurado |
| Solo logging | No intrusivo, bueno para monitoreo | No maneja errores, solo registra |
Preguntas frecuentes
¿Cuál es la diferencia entre errores y excepciones en PHP?
Los errores son problemas de bajo nivel como errores de sintaxis o de tipo, mientras que las excepciones son objetos lanzados que representan condiciones excepcionales. En PHP 7+, ambos implementan la interfaz Throwable, por lo que puedes capturar ambos con un solo bloque catch.
¿Debería usar try-catch para cada llamada a función?
No, eso lleva a un código demasiado defensivo. Captura excepciones solo cuando puedas manejarlas de manera significativa: registrar, reintentar o mostrar un mensaje amigable para el usuario. Deja que las excepciones burbujeen hasta un manejador central para casos inesperados.
¿Cómo registro errores sin exponer datos sensibles?
Sanitiza los mensajes de log eliminando contraseñas, tokens y datos personales. Usa logging estructurado con campos de contexto y configura tu biblioteca de logging para redactar claves sensibles. Además, asegúrate de que los archivos de log se almacenen de forma segura y el acceso esté restringido.
¿Listo para optimizar el manejo de errores en PHP? Comienza auditando tu configuración actual de reporte de errores e implementando un manejador de errores personalizado. Para una depuración rápida de payloads JSON o logs, prueba nuestro JSON Formatter para validar y formatear tus datos.