Menu
Coddy logo textTech

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.

challenge icon

Desafio

Fácil

Vamos 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 base OrderException que estende Exception. 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 classe OutOfStockException que estende OrderException. 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étodo getProductName(): string que retorna o nome do produto.
  • InvalidQuantityException.php — Crie uma classe InvalidQuantityException que estende OrderException. 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étodo getQuantity(): int que 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 InvalidQuantityException e imprima "Quantity error: [message] (value: [quantity])" usando a mensagem da exceção e o método getQuantity(). Em seguida, capture OutOfStockException e imprima "Stock error: [message] (product: [productName])" usando a mensagem da exceção e o método getProductName().

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()

?>
quiz iconTeste seus conhecimentos

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