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 exceções e boas práticas. O aluno aprenderá a lidar com erros de forma estruturada, criando código mais robusto e legível.
Nesta aula, vamos explorar o sistema de exceções do PHP, que permite tratar erros de forma controlada e elegante. As exceções são objetos que representam condições excepcionais no fluxo do programa, e seu uso adequado é fundamental para escrever código robusto e de fácil manutenção.
Diferentemente dos erros tradicionais (como warnings ou notices), as exceções podem ser capturadas e tratadas em pontos específicos do código, evitando que o programa termine abruptamente. Vamos aprender a usar os blocos try, catch e finally, a lançar exceções com throw, entender a hierarquia de classes de exceção e adotar boas práticas no tratamento de erros.
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 é opcional e sempre executa, independentemente de uma exceção ter sido lançada ou não, sendo útil para liberar recursos (como fechar arquivos ou conexões).
Exemplo básico:
try {
$arquivo = fopen("dados.txt", "r");
if (!$arquivo) {
throw new Exception("Não foi possível abrir o arquivo.");
}
// Processa o arquivo...
} catch (Exception $e) {
echo "Erro: " . $e->getMessage();
} finally {
if (isset($arquivo)) {
fclose($arquivo);
}
}
Você pode ter múltiplos blocos catch para capturar diferentes tipos de exceção. O PHP tentará corresponder a exceção lançada com cada bloco na ordem em que aparecem. Se nenhum bloco corresponder, a exceção se propaga para o chamador.
throw
A instrução throw é usada para lançar uma exceção. Ela pode ser seguida por qualquer objeto que implemente a interface Throwable, ou mais comumente, uma instância de Exception ou suas subclasses. Você também pode criar suas próprias classes de exceção estendendo Exception.
Exemplo de lançamento de exceção personalizada:
class SaldoInsuficienteException extends Exception {}
function sacar($valor, $saldo) {
if ($valor > $saldo) {
throw new SaldoInsuficienteException("Saldo insuficiente para saque de R$ $valor");
}
return $saldo - $valor;
}
try {
$novoSaldo = sacar(200, 100);
} catch (SaldoInsuficienteException $e) {
echo "Erro: " . $e->getMessage();
}
É importante lembrar que, após o throw, a execução da função atual é interrompida e o fluxo volta para o bloco catch mais próximo. Se não houver catch, a exceção se propaga até ser capturada ou causar um erro fatal.
Hierarquia de exceções
O PHP possui uma hierarquia de classes para exceções, baseada na interface Throwable. As principais classes são:
Exception– classe base para exceções comuns.Error– classe base para erros internos do PHP (como erros de tipo, argumentos inválidos).ErrorException– usada para converter erros tradicionais em exceções.
Além disso, existem subclasses especializadas, como InvalidArgumentException, RuntimeException, PDOException (para bancos de dados), entre outras. Conhecer essa hierarquia permite capturar exceções de forma mais precisa.
Exemplo de captura de múltiplos tipos:
try {
// código que pode lançar diferentes exceções
} catch (InvalidArgumentException $e) {
// trata argumento inválido
} catch (RuntimeException $e) {
// trata erro de execução
} catch (Exception $e) {
// captura qualquer outra exceção
}
Lembre-se de que, ao capturar uma exceção genérica como Exception, você pode estar escondendo erros que deveriam ser tratados de forma diferente. Por isso, prefira capturar exceções específicas sempre que possível.
Boas práticas
Ao trabalhar com exceções, siga estas recomendações:
- Use exceções para situações excepcionais: não as utilize para controle de fluxo normal (como substituir if/else).
- Seja específico: crie classes de exceção personalizadas para diferentes tipos de erro, facilitando o tratamento.
- Não engula exceções: evite blocos
catchvazios ou que apenas registrem o erro sem ação. No mínimo, registre o erro e relance se não puder tratar. - Libere recursos no finally: coloque a limpeza de recursos (arquivos, conexões) no bloco
finallypara garantir que sejam liberados mesmo em caso de exceção. - Documente as exceções: use docblocks (
@throws) para indicar quais exceções sua função pode lançar.
Exemplo de boa prática com finally:
function lerArquivo($caminho) {
$arquivo = fopen($caminho, 'r');
if (!$arquivo) {
throw new Exception("Falha ao abrir arquivo");
}
try {
// processa o arquivo
return fread($arquivo, filesize($caminho));
} finally {
fclose($arquivo);
}
}
Referências
Exercícios
Crie uma função que divide dois números. Se o divisor for zero, lance uma exceção com a mensagem "Divisão por zero não permitida". Capture a exceção e exiba a mensagem de erro.
✓ Resposta:function dividir($a, $b) { if ($b == 0) { throw new Exception("Divisão por zero não permitida"); } return $a / $b; } try { echo dividir(10, 0); } catch (Exception $e) { echo "Erro: " . $e->getMessage(); }Crie sua própria classe de exceção chamada
IdadeInvalidaException. Escreva uma função que verifica se a idade é maior que 0 e menor que 150; caso contrário, lance essa exceção. Capture-a e imprima a mensagem.✓ Resposta:class IdadeInvalidaException extends Exception {} function validarIdade($idade) { if ($idade <= 0 || $idade >= 150) { throw new IdadeInvalidaException("Idade inválida: $idade"); } return true; } try { validarIdade(200); } catch (IdadeInvalidaException $e) { echo "Erro: " . $e->getMessage(); }Escreva um código que abre um arquivo para leitura, lê seu conteúdo e o fecha. Use try/catch/finally para garantir que o arquivo seja fechado mesmo se ocorrer uma exceção durante a leitura.
✓ Resposta:$arquivo = fopen("exemplo.txt", "r"); if (!$arquivo) { die("Não foi possível abrir o arquivo."); } try { $conteudo = fread($arquivo, filesize("exemplo.txt")); echo $conteudo; } catch (Exception $e) { echo "Erro durante a leitura: " . $e->getMessage(); } finally { fclose($arquivo); }Crie uma função que aceita um array e um índice, e retorna o elemento naquele índice. Se o índice não existir, lance uma exceção do tipo
OutOfBoundsException(use a classe nativaOutOfBoundsException). Capture-a e exiba uma mensagem amigável.✓ Resposta:function obterElemento($array, $indice) { if (!array_key_exists($indice, $array)) { throw new OutOfBoundsException("Índice $indice não existe no array."); } return $array[$indice]; } try { $dados = ["a", "b", "c"]; echo obterElemento($dados, 5); } catch (OutOfBoundsException $e) { echo "Erro: " . $e->getMessage(); }Implemente um bloco try com múltiplos catch para capturar exceções dos tipos
InvalidArgumentExceptioneRuntimeException. Lance uma exceção de cada tipo em funções separadas e teste o tratamento.✓ Resposta:function funcao1() { throw new InvalidArgumentException("Argumento inválido em funcao1"); } function funcao2() { throw new RuntimeException("Erro de execução em funcao2"); } try { funcao1(); // funcao2(); // Descomente para testar } catch (InvalidArgumentException $e) { echo "Capturado InvalidArgumentException: " . $e->getMessage(); } catch (RuntimeException $e) { echo "Capturado RuntimeException: " . $e->getMessage(); } catch (Exception $e) { echo "Outra exceção: " . $e->getMessage(); }