Manejo de errores en PHP: de try/catch a soluciones industriales
\n\nCada desarrollador de PHP se enfrenta a errores. El try/catch básico es solo la punta del iceberg. En este artículo, analizaremos técnicas avanzadas de manejo de errores que transformarán un código frágil en un sistema robusto, capaz de reaccionar correctamente ante cualquier fallo.
1. Inmersión profunda en try/catch: bloque finally y múltiples catch
\n\nEl try/catch estándar es la base, pero muchos olvidan el bloque finally, que se ejecuta siempre, independientemente de si hubo una excepción. Es el lugar ideal para cerrar recursos (archivos, conexiones a la base de datos).
<?php\nfunction processFile(string $filename): void {\n $handle = fopen($filename, 'r');\n if (!$handle) {\n throw new \\RuntimeException("No se pudo abrir el archivo: $filename");\n }\n \n try {\n // Procesamiento del archivo\n while (($line = fgets($handle)) !== false) {\n // ... lógica de procesamiento\n }\n } catch (\\RuntimeException $e) {\n // Manejo específico de errores de entrada/salida\n error_log("Error de lectura del archivo: " . $e->getMessage());\n throw $e; // Relanzar hacia arriba\n } catch (\\Exception $e) {\n // Manejo de cualquier otra excepción\n error_log("Error desconocido: " . $e->getMessage());\n throw $e;\n } finally {\n fclose($handle); // Siempre cerrar el archivo\n }\n}\n?>\n\nImportante: El orden de los bloques catch importa. Las excepciones más específicas (por ejemplo, \\RuntimeException) deben ir antes que las generales (\\Exception). De lo contrario, el bloque específico nunca se ejecutará.
2. Clases de excepción personalizadas: semántica y jerarquía
\n\nLas excepciones integradas (PDOException, RuntimeException) a menudo no son lo suficientemente informativas. Cree su propia jerarquía de excepciones para separar los errores por tipo y simplificar su manejo.
\n\n<?php\n// Excepción base de la aplicación\nclass AppException extends \\Exception {}\n\n// Errores de validación\nclass ValidationException extends AppException {\n private array $errors;\n \n public function __construct(array $errors, string $message = "Error de validación") {\n parent::__construct($message, 422);\n $this->errors = $errors;\n }\n \n public function getErrors(): array {\n return $this->errors;\n }\n}\n\n// Errores de acceso\nclass AccessDeniedException extends AppException {\n public function __construct(string $message = "Acceso denegado") {\n parent::__construct($message, 403);\n }\n}\n\n// Ejemplo de uso\nfunction registerUser(array $data): void {\n $errors = [];\n \n if (empty($data['email'])) {\n $errors['email'] = 'El email es obligatorio';\n }\n if (empty($data['password'])) {\n $errors['password'] = 'La contraseña es obligatoria';\n }\n \n if (!empty($errors)) {\n throw new ValidationException($errors);\n }\n \n // Lógica de registro...\n}\n\ntry {\n registerUser(['email' => '']);\n} catch (ValidationException $e) {\n // Manejo específico: devolver JSON con errores\n http_response_code(422);\n echo json_encode(['errors' => $e->getErrors()]);\n} catch (AccessDeniedException $e) {\n http_response_code(403);\n echo json_encode(['error' => $e->getMessage()]);\n} catch (AppException $e) {\n // Manejo general para todos los demás errores de la aplicación\n http_response_code(500);\n echo json_encode(['error' => 'Error interno del servidor']);\n}\n?>\n\nConsejo: Use códigos HTTP (400, 403, 422, 500) como códigos de excepción; esto simplifica la integración con REST API.
\n\n3. Manejador global de errores: ErrorHandler y ShutdownHandler
\n\nNo todos los errores se pueden capturar con try/catch. Errores fatales (E_ERROR), advertencias (E_WARNING) y errores de análisis