11. Manejo de errores y excepciones
Manejo de Errores y Excepciones en PHP
Section titled “Manejo de Errores y Excepciones en PHP”El manejo adecuado de errores y excepciones es fundamental para crear aplicaciones robustas y confiables. PHP ofrece herramientas para detectar, manejar y responder a situaciones inesperadas de manera controlada.
¿Qué es un Error y qué es una Excepción?
Section titled “¿Qué es un Error y qué es una Excepción?”Aunque ambos indican que algo salió mal, hay diferencias importantes entre errores y excepciones:
Errores
Section titled “Errores”Los errores son problemas que ocurren durante la ejecución del código y generalmente son manejados por PHP automáticamente. Pueden detener la ejecución del script dependiendo de su gravedad.
<?php// Ejemplo de error: división por ceroecho 5 / 0; // Warning: Division by zero
// Ejemplo de error: variable no definidaecho $variableNoExiste; // Warning: Undefined variable
// Ejemplo de error fatal: llamar a función inexistentefuncionQueNoExiste(); // Fatal error: Uncaught Error?>Excepciones
Section titled “Excepciones”Las excepciones son objetos que representan condiciones excepcionales que puedes capturar y manejar en tu código. Te permiten controlar el flujo cuando algo sale mal.
<?php// Ejemplo de excepcióntry { throw new Exception("Algo salió mal");} catch (Exception $e) { echo "Error capturado: " . $e->getMessage();}// El script continúa ejecutándose?>Tipos Comunes de Errores en PHP
Section titled “Tipos Comunes de Errores en PHP”PHP clasifica los errores en diferentes niveles de gravedad:
1. Errores de Sintaxis (Parse Errors)
Section titled “1. Errores de Sintaxis (Parse Errors)”Ocurren cuando el código no sigue las reglas del lenguaje. PHP no puede ejecutar el script.
<?php// Error de sintaxis: falta punto y comaecho "Hola"echo "Mundo"; // Parse error
// Error de sintaxis: paréntesis sin cerrarif ($edad > 18 { echo "Mayor de edad";}?>2. Errores Lógicos
Section titled “2. Errores Lógicos”El código se ejecuta sin problemas técnicos, pero produce resultados incorrectos debido a la lógica del programa.
<?php// Error lógico: usar el operador incorrecto$edad = 15;
// Queremos verificar si es mayor de edad, pero usamos < en lugar de >=if ($edad < 18) { echo "Puedes votar"; // Esto es incorrecto} else { echo "No puedes votar";}
// Error lógico: cálculo incorrecto$precio = 100;$descuento = 20;
// Queremos aplicar 20% de descuento, pero restamos el valor directo$precioFinal = $precio - $descuento; // Debería ser: $precio * 0.8echo "Precio final: $precioFinal"; // Resultado incorrecto?>3. Errores de Ejecución (Runtime Errors)
Section titled “3. Errores de Ejecución (Runtime Errors)”Ocurren durante la ejecución del programa cuando algo inesperado sucede.
<?php// Warning: advertencia que no detiene el script
// División por cero$resultado = 10 / 0;echo "El script continúa"; // Se ejecuta
// Archivo no encontrado$archivo = fopen("noexiste.txt", "r");echo "Esto también se ejecuta";?><?php// Fatal Error: detiene la ejecución del script
// Llamar a función que no existecalcularTotal(); // Fatal error: Uncaught Error
// Este código NO se ejecutaecho "Esto nunca se mostrará";?><?php// Notice: notificación de bajo nivel
// Usar variable no definidaecho $nombre; // Notice: Undefined variable
// Usar índice de array inexistente$frutas = ["manzana", "pera"];echo $frutas[5]; // Notice: Undefined offset
// El script continúa normalmenteecho "El script sigue ejecutándose";?>Uso de try, catch y finally
Section titled “Uso de try, catch y finally”La estructura try-catch-finally permite manejar excepciones de forma controlada.
Sintaxis Básica
Section titled “Sintaxis Básica”<?phptry { // Código que puede generar una excepción $resultado = dividir(10, 0);
} catch (Exception $e) { // Código que se ejecuta si hay una excepción echo "Error: " . $e->getMessage();
} finally { // Código que SIEMPRE se ejecuta (opcional) echo "Operación finalizada";}
function dividir($a, $b) { if ($b == 0) { throw new Exception("No se puede dividir por cero"); } return $a / $b;}?>Ejemplo Práctico: Validación de Formulario
Section titled “Ejemplo Práctico: Validación de Formulario”<?phpfunction validarEdad($edad) { try { // Validar que sea un número if (!is_numeric($edad)) { throw new Exception("La edad debe ser un número"); }
// Validar que sea positivo if ($edad < 0) { throw new Exception("La edad no puede ser negativa"); }
// Validar rango razonable if ($edad > 120) { throw new Exception("La edad no es válida"); }
// Si todo está bien return "Edad válida: $edad años";
} catch (Exception $e) { return "Error de validación: " . $e->getMessage(); }}
// Probar la funciónecho validarEdad(25); // Edad válida: 25 añosecho validarEdad("abc"); // Error: La edad debe ser un númeroecho validarEdad(-5); // Error: La edad no puede ser negativaecho validarEdad(150); // Error: La edad no es válida?>Capturar Múltiples Tipos de Excepciones
Section titled “Capturar Múltiples Tipos de Excepciones”<?phpfunction procesarArchivo($nombreArchivo) { try { // Verificar si el archivo existe if (!file_exists($nombreArchivo)) { throw new Exception("Archivo no encontrado"); }
// Intentar leer el archivo $contenido = file_get_contents($nombreArchivo);
if ($contenido === false) { throw new RuntimeException("Error al leer el archivo"); }
// Intentar decodificar JSON $datos = json_decode($contenido, true);
if (json_last_error() !== JSON_ERROR_NONE) { throw new InvalidArgumentException("El archivo no contiene JSON válido"); }
return $datos;
} catch (InvalidArgumentException $e) { echo "Error de formato: " . $e->getMessage(); } catch (RuntimeException $e) { echo "Error de lectura: " . $e->getMessage(); } catch (Exception $e) { echo "Error general: " . $e->getMessage(); } finally { echo "\nProceso de lectura finalizado"; }}
// Usar la función$datos = procesarArchivo("datos.json");?>El Bloque finally
Section titled “El Bloque finally”El bloque finally se ejecuta siempre, haya o no una excepción. Es útil para limpiar recursos.
<?phpfunction conectarBaseDatos() { $conexion = null;
try { // Intentar conectar $conexion = new PDO("mysql:host=localhost;dbname=test", "usuario", "clave");
// Realizar operaciones $resultado = $conexion->query("SELECT * FROM usuarios");
return $resultado->fetchAll();
} catch (PDOException $e) { echo "Error de conexión: " . $e->getMessage(); return [];
} finally { // Cerrar conexión siempre (haya error o no) if ($conexion !== null) { $conexion = null; echo "Conexión cerrada"; } }}?>Lanzar Excepciones Manualmente con throw
Section titled “Lanzar Excepciones Manualmente con throw”Puedes lanzar tus propias excepciones usando throw para indicar que algo salió mal.
Lanzar Excepciones Básicas
Section titled “Lanzar Excepciones Básicas”<?phpfunction calcularDescuento($precio, $porcentaje) { // Validar precio if ($precio <= 0) { throw new Exception("El precio debe ser mayor a cero"); }
// Validar porcentaje if ($porcentaje < 0 || $porcentaje > 100) { throw new Exception("El porcentaje debe estar entre 0 y 100"); }
// Calcular descuento $descuento = $precio * ($porcentaje / 100); return $precio - $descuento;}
// Usar la función con try-catchtry { $precioFinal = calcularDescuento(100, 20); echo "Precio con descuento: $$precioFinal";
} catch (Exception $e) { echo "Error: " . $e->getMessage();}?>Crear Excepciones Personalizadas
Section titled “Crear Excepciones Personalizadas”Puedes crear tus propias clases de excepción para casos específicos:
<?php// Definir excepción personalizadaclass EdadInvalidaException extends Exception { public function mensajePersonalizado() { return "Error de edad: " . $this->getMessage(); }}
class UsuarioNoEncontradoException extends Exception { private $userId;
public function __construct($userId, $message = "Usuario no encontrado") { parent::__construct($message); $this->userId = $userId; }
public function getUserId() { return $this->userId; }}
// Usar excepciones personalizadasfunction registrarUsuario($nombre, $edad) { if ($edad < 18) { throw new EdadInvalidaException("Debes ser mayor de 18 años"); }
return "Usuario $nombre registrado correctamente";}
function buscarUsuario($id) { $usuarios = [1 => "Ana", 2 => "Juan", 3 => "María"];
if (!isset($usuarios[$id])) { throw new UsuarioNoEncontradoException($id); }
return $usuarios[$id];}
// Probar las funcionestry { echo registrarUsuario("Pedro", 15);} catch (EdadInvalidaException $e) { echo $e->mensajePersonalizado();}
try { $usuario = buscarUsuario(99);} catch (UsuarioNoEncontradoException $e) { echo $e->getMessage() . " (ID: " . $e->getUserId() . ")";}?>Ejemplo Completo: Sistema de Login
Section titled “Ejemplo Completo: Sistema de Login”<?php// Excepciones personalizadasclass CredencialesInvalidasException extends Exception {}class CuentaBloqueadaException extends Exception {}
class SistemaLogin { private $usuarios = [ 'admin' => ['password' => 'admin123', 'bloqueado' => false], 'usuario' => ['password' => 'user123', 'bloqueado' => false], 'bloqueado' => ['password' => 'pass123', 'bloqueado' => true] ];
public function login($username, $password) { try { // Verificar si el usuario existe if (!isset($this->usuarios[$username])) { throw new CredencialesInvalidasException("Usuario no existe"); }
$usuario = $this->usuarios[$username];
// Verificar si está bloqueado if ($usuario['bloqueado']) { throw new CuentaBloqueadaException("Tu cuenta está bloqueada"); }
// Verificar contraseña if ($usuario['password'] !== $password) { throw new CredencialesInvalidasException("Contraseña incorrecta"); }
// Login exitoso return [ 'success' => true, 'mensaje' => "Bienvenido, $username" ];
} catch (CuentaBloqueadaException $e) { return [ 'success' => false, 'tipo' => 'bloqueado', 'mensaje' => $e->getMessage() ];
} catch (CredencialesInvalidasException $e) { return [ 'success' => false, 'tipo' => 'credenciales', 'mensaje' => $e->getMessage() ];
} catch (Exception $e) { return [ 'success' => false, 'tipo' => 'general', 'mensaje' => 'Error inesperado: ' . $e->getMessage() ]; } }}
// Usar el sistema$sistema = new SistemaLogin();
// Intentos de login$resultado1 = $sistema->login('admin', 'admin123');print_r($resultado1); // Éxito
$resultado2 = $sistema->login('admin', 'wrongpass');print_r($resultado2); // Credenciales inválidas
$resultado3 = $sistema->login('bloqueado', 'pass123');print_r($resultado3); // Cuenta bloqueada?>Buenas Prácticas
Section titled “Buenas Prácticas”<?php// ✅ BUENAS PRÁCTICAS
// 1. Ser específico con las excepcionestry { // código} catch (PDOException $e) { // Manejar errores de base de datos} catch (InvalidArgumentException $e) { // Manejar argumentos inválidos} catch (Exception $e) { // Manejar otros errores}
// 2. Proporcionar mensajes descriptivosthrow new Exception("Error al procesar el pago: saldo insuficiente");
// 3. No capturar excepciones que no puedes manejartry { procesarPago();} catch (Exception $e) { // Solo captura si puedes hacer algo útil registrarError($e); notificarAdministrador($e);}
// 4. Usar finally para limpiezatry { $archivo = fopen("datos.txt", "r"); // procesar archivo} finally { if (isset($archivo)) { fclose($archivo); // Siempre cerrar el archivo }}
// ❌ MALAS PRÁCTICAS
// No hacer esto: capturar y no hacer nadatry { operacionImportante();} catch (Exception $e) { // Silenciar el error es peligroso}
// No hacer esto: excepciones genéricasthrow new Exception("Error"); // Poco descriptivo
// No hacer esto: usar excepciones para control de flujotry { foreach ($items as $item) { if ($item == $buscado) { throw new Exception("Encontrado"); } }} catch (Exception $e) { // Usar excepciones así es incorrecto}?>Resumen
Section titled “Resumen”- Errores son problemas manejados por PHP; excepciones son manejadas por tu código
- Los errores se clasifican en: sintaxis, lógicos y de ejecución
- Usa
try-catchpara capturar y manejar excepciones - El bloque
finallyse ejecuta siempre, útil para limpieza - Lanza excepciones con
throwcuando detectes problemas - Crea excepciones personalizadas para casos específicos
- Proporciona mensajes de error claros y descriptivos