Exceções no PHP

Exceções são usadas para mudar o curso normal de um script se um erro específico acontece.

O que é uma exceção

Com o PHP 5 foi criada uma nova forma, mais orientada a objetos, de lidar com erros.
Lidar com erros normalmente é usado para mudar o fluxo normal da execução do código se uma condição de erro específica acontece. Esta condição é chamada uma exceção.

O que normalmente acontece quando uma exceção é lançada é o seguinte:

O estado do código atual é salvado
A execução do código será desviada para um tradador de exceções específico. Dependendo da situação, o tratador poderá então deixar que a execução continue a partir do estado em que ela se encontrava, terminar a execução ou continuar a execução de um local diferente no código.
Nós veremos diferentes métodos de tratamento de erros:
  • O uso comum de Exceções
  • Criando um tratador de exceções customizado
  • Múltiplas exceções
  • Relançando uma exceção existente
  • Ajustando um tratador de exceção de nível superior
Nota: Exceções devem ser usadas apenas com condições de erro. Não devem ser usadas para desviar a execução do código em um ponto qualquer.

O uso comum de Exceções

Quando uma exceção é lançada, o código que segue não será executado e o PHP ira tentar encontrar o bloco "catch" correspondente.

Se um bloco "catch" não é encontrado, um erro fatal acontecerá com a mensagem "Uncaught Exception".

Vamos tentar lançar uma exceção sem um bloco catch correspondente:
   1:<?php
2://criar uma função com uma exceção.
3:function checkNum($number) {
4: if($number>1) {
5: throw new Exception("Value must be 1 or below");
6: }
7: return true;
8:}
9://chamamos a função
10:checkNum(2);
11:?>
O código acima produzirá um erro como o visto abaixo:
Fatal error: Uncaught exception 'Exception'
with message 'Value must be 1 or below' in C:\webfolder\test.php:6
Stack trace: #0 C:\webfolder\test.php(12):
checkNum(28) #1 {main} thrown in C:\webfolder\test.php on line 6

Try, throw e catch

Para evitar o erro do exemplo acima, nos precisamos de criar o código apropriado para lidar com uma exceção.

O código para lidar com a exceção deve incluir:
  • Try - Uma função que usa uma exceção deve estar dentro de um bloco "try". Se a exceção não acontece, o código irá continuar normalmente.
  • Throw - Esta é a forma como você lança uma exceção. Cada "throw" deve ter pelo menos um "catch".
  • Catch - Um bloco "catch" recebe a exceção que é um objeto contendo informações sobre a exceção.
Vamos tentar lançar uma exceção com um código válido:
   1:<?php
2://Uma função que lança uma exceção
3:function checkNum($number) {
4: if($number>1) {
5: throw new Exception("O valor deve ser 1 ou menos");
6: }
7: return true;
8:}
9:
10://Inicia a exceção centro de um bloco try
11:try {
12: checkNum(2);
13: //Se a exceção acontecer você não verá esta mensagem
14: echo 'Se você ver esta mensagem'
15: .'o número é 1 ou menos';
16:}
17:catch(Exception $e){
18: echo 'Mensagem: ' .$e->getMessage();
19:}
20:?>

Com o código acima você deve receber um erro com este:
Mensagem: O valor deve ser 1 ou menos

Explicação do exemplo:

O código acima lança uma exceção e a captura em seguida:
  1. A função checkNum() é criada. Ela checa se um número é maior que 1. Se é uma exceção é lançada.
  2. A função checkNum() é chamada dentro de um bloco try.
  3. A exceção dentro de checkNum() é lançada
  4. O bloco catch recebe a exceção e cria um objeto ($e) contendo informações sobre a exceção.
  5. A mensagem de erro da exceção é mostrada a partir de $e->getMessage() no objeto de exceção.
  6. Uma forma de não ter um bloco catch para cada possível throw é ter um bloco catch com uma exceção de nível superior para lidar com as exceções que não foram tratadas nos blocos catch anteriores.
Criando uma classe de exceção customizada

Criar um tratador de exceção customizado é bem simples. Nós simplesmente criamos uma classe especial com funções que serão chamadas quando uma exceção acontece no PHP. A classe deve ser uma extensão da classe Exception.

A classe exception herda as propriedades da classe Exception do PHP e você pode adicionar outras funções ou propriedades a ela.

Vamos criar uma classe exception:
   1:<?php
2:class CustomException extends Exception {
3: public function errorMessage() {
4: //Mensagem de erro
5: $errorMsg = 'Erro na linha '.$this->getLine()
6: .' em '.$this->getFile()
7: .': <b>'.$this->getMessage()
8: .'</b>Não é um e-mail válid';
9: return $errorMsg;
10: }
11:}
12:
13:$email = "someone@example...com";
14:try {
15: //Verifica o email.
16: if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
17: //lança uma exceção se não é válido
18: throw new CustomException($email);
19: }
20:}
21:catch (CustomException $e) {
22: //Mostra a mensagem customizada
23: echo $e->errorMessage();
24:}
25:?>

A nova classe é uma cópia da antiga classe Exception mais a função errorMessage(). Como esta classe estende a classe Exception ela herda as propriedades e métodos da classe antiga e nós podemos usar os métodos da classe superior. Nós podemos usar métodos como getLine(), getFile() e getMessage().

Explicando o exemplo:

O código acima lança uma exceção e captura com uma classe de exceção customizada:

  1. A classe CustomException é criada como uma extensão da classe Exception. Desta forma ela herda todos os métodos e propriedades da classe Exception.
  2. A função errorMessage() é criada. Esta função retorna uma mensagem de erro se um endereço de e-mail não é válido.
  3. A variável e-mail é ajustada como uma string que não é um e-mail válido.
  4. O bloco try é executado e uma exceção é lançada pois o endereço de e-mail é inválido.
  5. O bloco catch captura a exceção e mostra a mensagem de erro.
Multiplas Exceções

É possível que um script use multiplas exceções para verificar várias condições de erro.
   1:<?php
2:class CustomException extends Exception {
3: public function errorMessage() {
4: //Mensagem de erro
5: $errorMsg = 'Erro na linha '
6: .$this->getLine().' em '
7: .$this->getFile().': <b>'
8: .$this->getMessage()
9: .'</b> is not a valid E-Mail address';
10: return $errorMsg;
11: }
12:}
13:
14:$email = "someone@exemplo.com";
15:
16:try {
17: //Verifica o e-mail
18: if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
19: //lança uma exceção se o e-mail não é válido
20: throw new CustomException($email);
21: }
22:
23: //procura a palavra "exemplo" no endereço de email
24: if(strpos($email, "exemplo") !== FALSE) {
25: throw new Exception("$email é um e-mail de exemplo");
26: }
27:}
28:catch (CustomException $e) {
29: echo $e->errorMessage();
30:}
31:catch(Exception $e) {
32: echo $e->getMessage();
33:}
34:?>

Explicação do exemplo:
O código acima testa duas condições e lança uma exceção se qualquer das condições não é atendida:

  1. A classe CustomException é criada como uma extensão da classe Exception. Desta forma ela herda todos os métodos e propriedades de Exception.
  2. A função errorMessage() é criada. Esta função retorna uma mensagem de erro de um endereço de e-mail é inválido.
  3. A variável $email é ajustada como um texto que é um endereço de e-mail válido mas contém o texto "exemplo".
  4. O bloco try é executado e uma exceção não é lançada na primeira condição.
  5. A segunda condição lança uma exceção já que o e-mail contem o texto "exemplo".
  6. O bloco catch recebe a exceção e mostra a mensagem de erro.
  7. Se não houvesse o catch com a classe CustomExceptioin, apenas Exception, a exceção seria tratada ai.
Relançando exceções

Algumas vezes quando uma exceção é lançada, você pode querer trata-la de forma diferente da forma padrão. É possível lançar uma exceção uma segunda vez dentro do bloco catch.

Um script deve esconder erros do sistema dos usuários. Erros de sistema podem ser importantes para o programador, mas não tem interesse para os usuários. Para tornar as coisas mais fáceis para o usuário você pode relançar uma exceção com uma mensagem mais amigável.
   1:<?php
2:class CustomException extends Exception {
3: public function errorMessage() {
4: //Mensagem de erro
5: $errorMsg = 'Erro na linha '
6: .$this->getLine().' em '
7: .$this->getFile().': <b>'
8: .$this->getMessage()
9: .'</b> não é um endereço de e-mail válido';
10: return $errorMsg;
11: }
12:}
13:$email = "someone@exemplo.com";
14:try {
15: try {
16: //verifica a palavra "exemplo" no e-mail
17: if(strpos($email, "exemplo") !== FALSE) {
18: //lança uma exceção se o e-mail não é válido
19: throw new Exception($email);
20: }
21: }
22: catch(Exception $e) {
23: //relança a exceção
24: throw new customException($email);
25: }
26:}
27:catch (CustomException $e) {
28: //mostra a mensagem de erro
29: echo $e->errorMessage();
30:}
31:?>

Explicação do exemplo

O código acima testa se o endereço de e-mail contem o texto "exemplo". Se contém, uma exceção é relançada:

  1. A classe CustomException é criada como uma extensão da classe Exception.
  2. A variável $email é ajustada como um e-mail válido, mas contém o texto "exemplo".
  3. O bloco try contem outro bloco try para que se torne possível relançar a exceção.
  4. A exceção é lançada já que o e-mail contem a palavra "exemplo".
  5. O bloco catch captura a exceção e relança uma CustomException.
Ajustando tratadores de exceções de nível superior

A função set_exception_handler() ajusta uma função definida pelo usuário para lidar com todas as exceções não capturadas.
   1:<?php
2:function myException($exception)
3:{
4: echo "<b>Exception:</b> " , $exception->getMessage();
5:}
6:set_exception_handler('myException');
7:throw new Exception('Exceção não capturada');
8:?>

A saída do código acima deve ser algo assim:
Exception: Exceção não capturada
No código acima não há um bloco "catch". Assim o tratador de exceções de nível superior é chamado. Esta função deve ser usada para capturar exceções não caputradas em blocos try..catch.

Regras para exceções

  • O código pode ser limitado por blocos try..catch para capturar exceções em potencial
  • Cada bloco try deve ter um bloco catch para cada tipo de exceção possível dentro daquele bloco.
  • Múltiplos blocos catch podem ser usados para capturar diferentes classes de exceções.
  • Exceções podem ler lançadas (ou relançadas) em um bloco catch dentro de um bloco try.
  • Uma regra simples: Se você lançar alguma coisa, você tem que capturar.
Fonte

6 comentários:

  1. cara realmente muito bom seu post, estava procurando algo para deixar ainda mais dinâmico meu código e este post uma boa direção vlw.

    ResponderExcluir
  2. Ótimo post, parabéns, sou programador Java e em minha nova empresa tenho que trabalhar por um tempo com php, dai to iniciando meus estudos em php mais quero manter meus códigos OO. Coisa que eu não sabia que era possível em php antes de ter q começar a estuda-lo, estou me surpreendendo com esta linguagem, ela é muito poderosa se for usada com sabedoria e com padrões a serem seguidos(pois ela proporciona muita liberdade para programar), ótima linguagem ótimo post. Agora já posso tratar minhas exceptions praticamente da mesma maneira que faço em java.

    Vlw.

    ResponderExcluir
  3. Olá Avaiano, eu também tenho um histórico de programação Java mas hoje trabalho quase que exclusivamente com PHP. Como você disse, PHP é uma linguagem que precisa de ser usada com sabedoria pois ela é realmente bastante flexivel. A implementação OO em PHP, pelo menos se você comparar com Java, não é tão robusta mas acredito que é bem capaz de oferecer o que é necessário.

    ResponderExcluir
  4. Excelente artigo. Estou adorando e estudando todos os dias. Parabéns!

    ResponderExcluir