Hierarquia de Exceções Personalizadas
Parte da seção Programação Orientada a Objetos do Journey de PHP da Coddy — lição 67 de 91.
Embora as classes de exceção integradas do PHP cubram cenários comuns, aplicações reais frequentemente precisam de tipos de erro mais específicos. Criar uma hierarquia de exceções personalizada permite definir exceções que correspondam ao seu domínio e capturá-las em diferentes níveis de especificidade.
Para criar uma exceção personalizada, basta estender a classe Exception ou uma de suas subclasses:
<?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;
}
}
Observe como InsufficientFundsException estende PaymentException, não Exception diretamente. Isso cria uma hierarquia onde você pode capturar todas as exceções relacionadas a pagamentos com um único bloco catch, ou tratar as específicas individualmente.
Exceções personalizadas podem incluir propriedades e métodos adicionais relevantes ao erro. Aqui, getShortfall() fornece informações úteis que uma exceção genérica não poderia oferecer:
<?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;
}
}
Uma hierarquia de exceções bem projetada torna seu código mais fácil de manter. Ao capturar exceções, você pode ser tão abrangente ou específico quanto necessário, e os próprios objetos de exceção carregam um contexto significativo sobre o que deu errado.
Desafio
FácilVamos construir um sistema de processamento de pedidos com uma hierarquia de exceções personalizadas que lida com diferentes tipos de falhas de pedidos. Você criará exceções que carregam um contexto significativo sobre o que deu errado, tornando o tratamento de erros mais preciso e informativo.
Você organizará seu código em quatro arquivos:
OrderException.php— Crie uma classe baseOrderExceptionque estendeException. Isso serve como o pai para todas as exceções relacionadas a pedidos, permitindo que você capture qualquer problema de pedido com um único bloco catch quando necessário.OutOfStockException.php— Crie uma classeOutOfStockExceptionque estendeOrderException. Esta exceção deve aceitar um nome de produto e a quantidade solicitada em seu construtor. Use a promoção de construtor (constructor promotion) para essas propriedades privadas. Chame o construtor pai com uma mensagem no formato"[productName] is out of stock (requested: [quantity])". Adicione um métodogetProductName(): stringque retorna o nome do produto.InvalidQuantityException.php— Crie uma classeInvalidQuantityExceptionque estendeOrderException. Esta exceção deve aceitar um valor de quantidade em seu construtor usando a promoção de construtor como uma propriedade privada. Chame o construtor pai com a mensagem"Invalid quantity: [quantity]". Adicione um métodogetQuantity(): intque retorna o valor da quantidade.main.php— Inclua todos os três arquivos de exceção. Você receberá duas entradas: um nome de produto e uma quantidade (como uma string que precisa ser convertida para um inteiro).Com base no valor da quantidade, lance a exceção apropriada:
- Se a quantidade for menor que 1, lance uma
InvalidQuantityException - Se a quantidade for maior que 100, lance uma
OutOfStockException(simulando estoque insuficiente) - Caso contrário, imprima
"Order placed: [quantity]x [productName]"
Capture as exceções usando a hierarquia. Primeiro capture
InvalidQuantityExceptione imprima"Quantity error: [message] (value: [quantity])"usando a mensagem da exceção e o métodogetQuantity(). Em seguida, captureOutOfStockExceptione imprima"Stock error: [message] (product: [productName])"usando a mensagem da exceção e o métodogetProductName().- Se a quantidade for menor que 1, lance uma
Esta hierarquia permite que você trate exceções específicas individualmente, mantendo a opção de capturar todos os problemas relacionados a pedidos por meio da classe base OrderException. Os métodos personalizados em cada exceção fornecem um contexto que uma exceção genérica não poderia oferecer.
Folha de consulta
Crie exceções personalizadas estendendo a classe Exception ou suas subclasses:
<?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;
}
}
Exceções personalizadas podem formar uma hierarquia ao estender outras exceções personalizadas. Isso permite capturar exceções em diferentes níveis de especificidade — capture todas as exceções relacionadas a pagamentos com PaymentException, ou trate as específicas como InsufficientFundsException individualmente.
Exceções personalizadas podem incluir propriedades e métodos adicionais para fornecer contexto sobre o erro:
<?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;
}
}
Use a promoção de construtor para definir propriedades diretamente nos parâmetros do construtor. Chame parent::__construct() para definir a mensagem da exceção.
Experimente você mesmo
<?php
// Incluir todos os arquivos de exceção
require_once 'OrderException.php';
require_once 'OutOfStockException.php';
require_once 'InvalidQuantityException.php';
// Ler entrada
$productName = trim(fgets(STDIN));
$quantity = intval(trim(fgets(STDIN)));
// TODO: Usar try-catch para lidar com o processamento do pedido
// Dentro do bloco try:
// - Se quantity < 1, lançar InvalidQuantityException
// - Se quantity > 100, lançar OutOfStockException
// - Caso contrário, imprimir "Order placed: [quantity]x [productName]"
//
// Capturar InvalidQuantityException primeiro:
// - Imprimir "Quantity error: [message] (value: [quantity])" usando getMessage() e getQuantity()
//
// Capturar OutOfStockException:
// - Imprimir "Stock error: [message] (product: [productName])" usando getMessage() e getProductName()
?>Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave $thisMétodosPropriedadesConstrutor (__construct)Destrutor (__destruct)Recapitulação - Calculadora Simples4Herança
Herança BásicaA Palavra-chave parent::Sobrescrita de MétodoA Palavra-chave finalClasses AbstratasRecapitulação - Hierarquia de Funcionários7Encapsulamento
Público, Protegido, PrivadoModificadores de Acesso em DetalhesGetters e SettersOcultação de InformaçãoPromoção de Construtor (8.0)Recapitulação - Sistema de Registro de Alunos10Conceitos Avançados de POO
Composição vs HerançaInjeção de DependênciaClasses AnônimasEnums (PHP 8.1)Fibers (PHP 8.1)Mergulho Profundo em Clonagem de ObjetosGeradores e Iteradores13Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Repository2Namespaces e Autoloading
Introdução a NamespacesA Palavra-chave usePadrão de Autoloading PSR-4Autoloader do ComposerRecapitulação - Projeto Organizado5Interfaces e Contratos
Introdução a InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesInterface vs Classe AbstrataType Hinting com InterfacesRecapitulação - Calculadora de Formas8Métodos Mágicos
Introdução aos Métodos Mágicos__toString e __debugInfo__get, __set, __isset, __unset__call e __callStatic__clone e Clonagem de Objetos__serialize e __unserializeRecapitulação - Coleção Personalizada11Sistema de Tipos e Tratamento de Erros
Declarações de TiposTipos AnuláveisTipos de União e InterseçãoClasses de ExceçãoHierarquia de Exceções PersonalizadasTry, Catch, FinallyRecapitulação - Validador de Formulário14Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e Usuário3Propriedades de Classe
Propriedades de Instância vs EstáticasConstantes em ClassesMétodos e Propriedades EstáticosPropriedades Privadas e ProtegidasPropriedades Readonly (PHP 8.1)Recapitulação - Gerenciador de Conta Bancária6Polimorfismo
Sobrescrita de Métodos RevisitadaPolimorfismo via InterfacesType Hinting e Union TypesLate Static BindingRecapitulação - Processador de Pagamentos9Traits
Introdução a TraitsUsando Múltiplos TraitsResolução de Conflitos de TraitsMétodos Abstratos em TraitsTraits vs Herança12Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy