Exceções
Esta aula aborda o tratamento de exceções em PHP, cobrindo os blocos try/catch/finally, a instrução throw, a hierarquia de classes de exceção e boas práticas para escrever código robusto e legível.
Exceções são mecanismos poderosos para lidar com erros em tempo de execução de forma controlada. Em PHP, o tratamento de exceções permite que você capture e responda a condições excepcionais sem interromper abruptamente o fluxo do programa. Nesta aula, exploraremos como usar try/catch/finally, como lançar exceções com throw, a hierarquia de classes de exceção e as melhores práticas para escrever código resiliente.
Dominar exceções é essencial para criar aplicações robustas que lidam graciosamente com falhas de banco de dados, arquivos ausentes, entradas inválidas e outros imprevistos. Vamos mergulhar nos detalhes.
try/catch/finally
O bloco try envolve o código que pode lançar uma exceção. Se uma exceção for lançada dentro do bloco try, a execução é transferida para o bloco catch correspondente, onde você pode tratar o erro. O bloco finally é executado sempre, independentemente de uma exceção ter sido lançada ou não, sendo útil para liberar recursos como conexões de banco de dados ou arquivos abertos.
Exemplo básico:
try {
$file = fopen("arquivo.txt", "r");
if (!$file) {
throw new Exception("Não foi possível abrir o arquivo.");
}
// leitura do arquivo...
} catch (Exception $e) {
echo "Erro: " . $e->getMessage();
} finally {
if (isset($file) && $file) {
fclose($file);
}
}
Você pode ter múltiplos blocos catch para tratar diferentes tipos de exceção, e o PHP executará o primeiro que corresponder à exceção lançada. O finally é opcional, mas muito útil para limpeza.
throw
A instrução throw é usada para lançar uma exceção. Você pode lançar qualquer objeto que implemente a interface Throwable, geralmente uma instância de Exception ou suas subclasses. O throw interrompe imediatamente a execução da função atual e retorna ao bloco catch mais próximo.
Exemplo de lançamento condicional:
function dividir($a, $b) {
if ($b == 0) {
throw new InvalidArgumentException("Divisão por zero não permitida.");
}
return $a / $b;
}
try {
echo dividir(10, 0);
} catch (InvalidArgumentException $e) {
echo "Erro: " . $e->getMessage();
}
É possível relançar uma exceção dentro de um bloco catch usando throw $e;, o que permite que o erro seja tratado em um nível superior.
Hierarquia de exceções
O PHP possui uma hierarquia de classes de exceção encabeçada pela interface Throwable, que é implementada por Exception e Error. A partir do PHP 7, erros fatais (como erros de tipo) são lançados como Error, que também pode ser capturado. Abaixo de Exception, existem várias subclasses nativas, como InvalidArgumentException, RuntimeException, OutOfBoundsException, etc.
Você pode criar suas próprias classes de exceção estendendo Exception ou qualquer outra classe da hierarquia:
class MinhaExcecao extends Exception {}
try {
throw new MinhaExcecao("Erro personalizado.");
} catch (MinhaExcecao $e) {
echo "Capturado: " . $e->getMessage();
}
Entender a hierarquia ajuda a capturar exceções no nível adequado de especificidade, evitando capturas genéricas demais ou específicas demais.
Boas práticas
- Seja específico: Capture exceções específicas em vez de usar
catch (Exception $e)genérico, a menos que seja realmente necessário. - Não use exceções para controle de fluxo normal: Exceções são para situações excepcionais, não para substituir estruturas condicionais.
- Documente as exceções: Use docblocks para indicar quais exceções uma função pode lançar, facilitando o uso por outros desenvolvedores.
- Libere recursos no finally: Sempre feche arquivos, conexões de banco de dados ou outros recursos no bloco
finallypara evitar vazamentos. - Não silencie exceções: Evite blocos
catchvazios ou que apenas registram e continuam sem ação adequada.
Além disso, considere criar exceções personalizadas para diferentes domínios da sua aplicação, como DatabaseException ou ValidationException, para facilitar o tratamento granular.
Referências
- PHP Manual: Exceções
- PHP Manual: Throwable Interface
- PHP Manual: Exception Class
- PHP Manual: Errors in PHP 7
- PSR-3: Logger Interface (boas práticas de logging)
Exercícios
- Crie uma função que valida uma idade (deve ser inteiro entre 0 e 150). Se a idade for inválida, lance uma
InvalidArgumentExceptioncom uma mensagem descritiva. Capture a exceção e exiba a mensagem.✓ Resposta:function validarIdade($idade) { if (!is_int($idade) || $idade < 0 || $idade > 150) { throw new InvalidArgumentException("Idade deve ser um inteiro entre 0 e 150."); } return true; } try { validarIdade(200); } catch (InvalidArgumentException $e) { echo "Erro: " . $e->getMessage(); } - Escreva um código que tenta abrir um arquivo para leitura. Se o arquivo não existir, capture a exceção e crie o arquivo vazio. Use
finallypara fechar o arquivo se ele foi aberto.✓ Resposta:$arquivo = "dados.txt"; $handle = null; try { $handle = fopen($arquivo, "r"); if (!$handle) { throw new Exception("Não foi possível abrir o arquivo."); } // leitura... } catch (Exception $e) { // Cria o arquivo vazio $handle = fopen($arquivo, "w"); fclose($handle); echo "Arquivo criado."; } finally { if ($handle) { fclose($handle); } } - Crie uma classe de exceção personalizada chamada
SaldoInsuficienteExceptionque estendeException. Em uma funçãosacar($valor), lance essa exceção se o saldo for menor que o valor. Capture-a e exiba uma mensagem.✓ Resposta:class SaldoInsuficienteException extends Exception {} $saldo = 100; function sacar($valor) { global $saldo; if ($valor > $saldo) { throw new SaldoInsuficienteException("Saldo insuficiente para saque de R$ $valor."); } $saldo -= $valor; } try { sacar(150); } catch (SaldoInsuficienteException $e) { echo "Erro: " . $e->getMessage(); } - Implemente uma função que divide dois números e capture
DivisionByZeroError(que é um erro, não exceção). Exiba uma mensagem amigável.✓ Resposta:function dividir($a, $b) { if ($b == 0) { throw new DivisionByZeroError("Divisão por zero."); } return $a / $b; } try { echo dividir(10, 0); } catch (DivisionByZeroError $e) { echo "Erro: Não é possível dividir por zero."; } - Crie um bloco try/catch que capture tanto
InvalidArgumentExceptionquantoRuntimeExceptionem catches separados. Dentro do try, lance umaRuntimeExceptioncom uma mensagem.✓ Resposta:try { throw new RuntimeException("Erro de execução."); } catch (InvalidArgumentException $e) { echo "InvalidArgumentException: " . $e->getMessage(); } catch (RuntimeException $e) { echo "RuntimeException: " . $e->getMessage(); }