Desafio PHP

Múltiplos catch e exceções encadeadas

Enunciado

Crie duas exceções personalizadas: SaldoInsuficienteException e ValorInvalidoException, ambas estendendo Exception. Implemente uma classe ContaBancaria com saldo inicial e método sacar($valor). O método deve lançar ValorInvalidoException se $valor <= 0 e SaldoInsuficienteException se $valor > saldo. No código principal, realize três saques: -50, 200 (saldo inicial 100) e 50. Use um único bloco try com múltiplos catch para tratar cada exceção e um finally que imprime "Operação finalizada" após cada tentativa (dica: use um laço ou repita o try/catch).

Requisitos

  • As classes SaldoInsuficienteException e ValorInvalidoException devem estender Exception.
  • O método sacar deve lançar as exceções conforme as condições.
  • O código principal deve ter múltiplos catch para cada tipo de exceção.
  • O finally deve ser executado após cada tentativa de saque.
  • A saída deve mostrar as mensagens de erro e o saldo final após o saque bem-sucedido.

Código inicial

<?php

// Defina as exceções personalizadas

class ContaBancaria {
    private $saldo;

    public function __construct($saldoInicial) {
        $this->saldo = $saldoInicial;
    }

    public function sacar($valor) {
        // Lance as exceções apropriadas
        // Atualize o saldo se o saque for válido
    }

    public function getSaldo() {
        return $this->saldo;
    }
}

// Crie uma conta com saldo 100
// Realize os saques -50, 200, 50 usando try/catch/finally

Saída esperada

Valor inválido: -50
Operação finalizada
Saldo insuficiente: 200 (saldo atual: 100)
Operação finalizada
Saque de 50 realizado. Novo saldo: 50
Operação finalizada
Ver dica

Use um array com os valores de saque e um laço foreach para repetir o bloco try/catch/finally. No catch, capture cada exceção específica e imprima a mensagem.

Mostrar solução
<?php

class SaldoInsuficienteException extends Exception {}
class ValorInvalidoException extends Exception {}

class ContaBancaria {
    private $saldo;

    public function __construct($saldoInicial) {
        $this->saldo = $saldoInicial;
    }

    public function sacar($valor) {
        if ($valor <= 0) {
            throw new ValorInvalidoException("Valor inválido: $valor");
        }
        if ($valor > $this->saldo) {
            throw new SaldoInsuficienteException("Saldo insuficiente: $valor (saldo atual: {$this->saldo})");
        }
        $this->saldo -= $valor;
        echo "Saque de $valor realizado. Novo saldo: {$this->saldo}\n";
    }

    public function getSaldo() {
        return $this->saldo;
    }
}

$conta = new ContaBancaria(100);
$saques = [-50, 200, 50];

foreach ($saques as $valor) {
    try {
        $conta->sacar($valor);
    } catch (ValorInvalidoException $e) {
        echo $e->getMessage() . "\n";
    } catch (SaldoInsuficienteException $e) {
        echo $e->getMessage() . "\n";
    } finally {
        echo "Operação finalizada\n";
    }
}

Passo a passo

  1. Definimos duas exceções personalizadas que estendem Exception.
  2. A classe ContaBancaria tem um saldo privado e um método sacar.
  3. No sacar, verificamos primeiro se o valor é inválido (<= 0) e lançamos ValorInvalidoException.
  4. Depois, verificamos se o valor excede o saldo e lançamos SaldoInsuficienteException.
  5. Se passar, subtraímos do saldo e imprimimos a mensagem de sucesso.
  6. No código principal, criamos uma conta com saldo 100 e um array de saques.
  7. Para cada saque, usamos try para chamar sacar, com catch para cada tipo de exceção e finally para imprimir "Operação finalizada".

Por que funciona

O uso de múltiplos catch permite tratar cada exceção de forma específica. O finally é executado após cada iteração, independentemente de erro. O laço repete o bloco para cada valor, simulando tentativas sequenciais.

Erros comuns

  • Ordem dos catch: se capturar Exception antes das específicas, as específicas nunca serão alcançadas. Sempre do mais específico para o mais genérico.
  • Esquecer de atualizar o saldo: se não fizer $this->saldo -= $valor, o saldo não muda.
  • Lançar exceção com tipo errado: por exemplo, lançar Exception genérica em vez das personalizadas, o que impediria a captura específica.

Outra forma de resolver

Você pode usar um único catch (Throwable $e) e verificar o tipo com instanceof, mas múltiplos catch são mais limpos e idiomáticos.

Saída esperada

Valor inválido: -50
Operação finalizada
Saldo insuficiente: 200 (saldo atual: 100)
Operação finalizada
Saque de 50 realizado. Novo saldo: 50
Operação finalizada