Jerarquía de excepciones personalizada
Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy. Lección 67 de 91.
Aunque las clases de excepciones integradas de PHP cubren situaciones comunes, las aplicaciones reales a menudo necesitan tipos de error más específicos. Crear una jerarquía de excepciones personalizada te permite definir excepciones que se ajusten a tu dominio y capturarlas en distintos niveles de especificidad.
Para crear una excepción personalizada, simplemente extiende la clase Exception o una de sus subclases:
<?php
class PaymentException extends Exception {}
class InsufficientFundsException extends PaymentException {
public function __construct(
private float $required,
private float $available
) {
parent::__construct("Need $required but only $available available");
}
public function getShortfall(): float {
return $this->required - $this->available;
}
}
Observa cómo InsufficientFundsException extiende PaymentException, no directamente Exception. Esto crea una jerarquía en la que puedes capturar todas las excepciones relacionadas con pagos con un solo bloque catch, o manejar las específicas individualmente.
Las excepciones personalizadas pueden incluir propiedades y métodos adicionales relevantes para el error. Aquí, getShortfall() proporciona información útil que una excepción genérica no podría ofrecer:
<?php
class Wallet {
public function __construct(private float $balance) {}
public function pay(float $amount): void {
if ($amount > $this->balance) {
throw new InsufficientFundsException($amount, $this->balance);
}
$this->balance -= $amount;
}
}
Una jerarquía de excepciones bien diseñada hace que tu código sea más fácil de mantener. Al capturar excepciones, puedes ser tan general o específico como necesites, y los propios objetos de excepción contienen información significativa sobre lo que salió mal.
Desafío
FácilConstruyamos un sistema de procesamiento de pedidos con una jerarquía de excepciones personalizada que gestione distintos tipos de fallos en los pedidos. Crearás excepciones que contengan contexto significativo sobre lo que salió mal, haciendo que la gestión de errores sea más precisa e informativa.
Organizarás tu código en cuatro archivos:
OrderException.php: Crea una clase baseOrderExceptionque extiendaException. Esta sirve como clase principal para todas las excepciones relacionadas con pedidos, lo que te permite capturar cualquier problema de un pedido con un único bloque catch cuando sea necesario.OutOfStockException.php: Crea una claseOutOfStockExceptionque extiendaOrderException. Esta excepción debe aceptar un nombre de producto y la cantidad solicitada en su constructor. Usa la promoción de constructor para estas propiedades privadas. Llama al constructor principal con un mensaje en el formato"[productName] is out of stock (requested: [quantity])". Añade un métodogetProductName(): stringque devuelva el nombre del producto.InvalidQuantityException.php: Crea una claseInvalidQuantityExceptionque extiendaOrderException. Esta excepción debe aceptar un valor de cantidad en su constructor mediante la promoción de constructor como propiedad privada. Llama al constructor principal con el mensaje"Invalid quantity: [quantity]". Añade un métodogetQuantity(): intque devuelva el valor de la cantidad.main.php: Incluye los tres archivos de excepciones. Recibirás dos entradas: un nombre de producto y una cantidad (como una cadena que debe convertirse en un entero).Según el valor de la cantidad, lanza la excepción correspondiente:
- Si la cantidad es menor que 1, lanza una
InvalidQuantityException - Si la cantidad es mayor que 100, lanza una
OutOfStockException(simulando existencias insuficientes) - De lo contrario, imprime
"Order placed: [quantity]x [productName]"
Captura las excepciones mediante la jerarquía. Primero captura
InvalidQuantityExceptione imprime"Quantity error: [message] (value: [quantity])"usando el mensaje de la excepción y el métodogetQuantity(). Después capturaOutOfStockExceptione imprime"Stock error: [message] (product: [productName])"usando el mensaje de la excepción y el métodogetProductName().- Si la cantidad es menor que 1, lanza una
Esta jerarquía te permite gestionar excepciones específicas individualmente y, al mismo tiempo, conservar la opción de capturar todos los problemas relacionados con pedidos mediante la clase base OrderException. Los métodos personalizados de cada excepción proporcionan un contexto que una excepción genérica no podría ofrecer.
Pruébalo tú mismo
<?php
// Incluir todos los archivos de excepciones
require_once 'OrderException.php';
require_once 'OutOfStockException.php';
require_once 'InvalidQuantityException.php';
// Leer la entrada
$productName = trim(fgets(STDIN));
$quantity = intval(trim(fgets(STDIN)));
// TODO: Usa try-catch para manejar el procesamiento del pedido
// Dentro del bloque try:
// - Si quantity < 1, lanza InvalidQuantityException
// - Si quantity > 100, lanza OutOfStockException
// - De lo contrario, imprime "Order placed: [quantity]x [productName]"
//
// Captura InvalidQuantityException primero:
// - Imprime "Quantity error: [message] (value: [quantity])" usando getMessage() y getQuantity()
//
// Captura OutOfStockException:
// - Imprime "Stock error: [message] (product: [productName])" usando getMessage() y getProductName()
?>Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de la POO
Archivos externosIntroducción a la POOClases frente a objetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Repaso: calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasRepaso - Jerarquía de empleados7Encapsulación
Público, protegido, privadoModificadores de acceso en profundidadGetters y settersOcultación de la informaciónPromoción del constructor (8.0)Repaso: sistema de registros de estudiantes10Conceptos avanzados de OOP
Composición vs. herenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibras (PHP 8.1)Análisis profundo de la clonación de objetosGeneradores e iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Namespaces y carga automática
Introducción a los NamespacesLa palabra clave useEstándar de carga automática PSR-4Autoloader de ComposerRepaso: proyecto organizado5Interfaces y contratos
Introducción a las interfacesImplementación de interfacesImplementación de múltiples interfacesInterfaces vs. clase abstractaTipado con interfacesRepaso - Calculadora de formas8Métodos mágicos
Introducción a los métodos mágicos__toString y __debugInfo__get, __set, __isset, __unset__call y __callStatic__clone y clonación de objetos__serialize y __unserializeRepaso: colección personalizada11Sistema de tipos y manejo de errores
Declaraciones de tiposTipos anulablesTipos unión e intersecciónClases de excepcionesJerarquía de excepciones personalizadaTry, Catch, FinallyRepaso: validador de formularios3Propiedades de clase
Propiedades de instancia vs. estáticasConstantes en clasesMétodos y propiedades estáticosPropiedades privadas y protegidasPropiedades de solo lectura (PHP 8.1)Repaso: gestor de cuentas bancarias6Polimorfismo
Repaso de la sobrescritura de métodosPolimorfismo mediante interfacesAnotaciones de tipo y tipos uniónEnlace estático tardíoRepaso: procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos entre TraitsMétodos abstractos en TraitsTraits frente a la herencia12Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón StrategyPractica por tu cuenta: Compilador de PHP online