Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Manoel Barros

Exception, Try e Catch

Recommended Posts

Tudo bem pessoal

 

To estudando Exception e não entendo qual o motivo de colocar outros nomes em Exception exemplo:

Para dispara um Exception seria da seguinte forma

throw new Exception('acontenceu um erro');

Só que eu tenho visto pela net, Exception com outros nomes vejam:

throw new LogicException('logica errada');
// e tbm
throw new InvalidArgumentException('Argumento invalido');

1. Qual o objetivo de colocar outros nomes em uma Exception?

2. Aproveitando o tópico qual a hora certa de aplicar: try e catch? e para que serve exatamente eles?

Compartilhar este post


Link para o post
Compartilhar em outros sites

1. Qual o objetivo de colocar outros nomes em uma Exception?

O que você precisa entender é que há tipos de exceções, como as classes citadas por você mesmo (LogicException, RuntimeException...). Todas estas classes são filhas da superclasse (classe pai) Exception.

 

A vantagem de se usar é facilitar o manuseio, por exemplo:

class Foo {

    public function bar($value) {
        if (!is_int($value)) {
            throw new InvalidArgumentException('Value is not integer');
        }

        if ($value < 0) {
            throw new LogicException('Value is negative');
        }
    }
}

A lógica contida no exemplo é apenas uma foma demonstrativa..

 

O método bar() pode disparar dois tipos de exceções, LogicException e InvalidArgumentException. Agora supondo que eu deseje capturar apenas as exceções do tipo LogicException caso alguma seja disparada, isso é fácil.

try {
    (new Foo)->bar(-10);
} catch(LogicException $e) {
    echo $e->getMessage(); // Value is negative
}

Eu também poderia ter mais um bloco catch para tratar o tipo InvalidArgumentException:

try {
    (new Foo)->bar('string');
} catch(LogicException $e) {
    echo $e->getMessage();
} catch(InvalidArgumentException $e) {
    echo $e->getMessage(); // Value is not integer
}

Se lembra quando eu disse que todos esses tipos de exceção são classes filhas da superclasse Exception?

try {
   throw new RuntimeException('...');
} catch(RuntimeException $e) {
    var_dump($e instanceof Exception); // bool(true)
}

E caso você queira capturar qualquer um dos tipos de exceção faria:

try {
    (new Foo)->bar(-10);
} catch(Exception $e) {
    echo $e->getMessage(); // Value is negative
    var_dump($e instanceof LogicException); // bool(true)
}

Mas você também pode criar os seus próprios tipos de exceção estendendo qualquer classe de exceção tando a superclasse Exception quanto as classes filhas:

class MyException extends Exception {

}

Usando..

throw new MyException('Message...');

 

2. Aproveitando o tópico qual a hora certa de aplicar: try e catch?

Sempre que quiser tratar/capturar exceções.. exceções não capturadas como você já deve saber interrompem a execução.

 

 

Espero que tenha entendido ;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu acredito que Marcielo tenha respondido bem o que são e como usar. Por isso não entrarei nos detalhes que ele já apresentou. Mas, ao meu ver, não ficou claro o porque usar. Para entender melhor, é melhor você começa pelo o que são exceções, falando no escopo de um software. Pra isso, tem que entender o que é o "caminho feliz".

Caminho feliz nada mais é que o trajeto ideal que um sistema deve seguir. Vamos ao o login do sistema e os passos do "caminho feliz" (entenda como {u} ação do usuário e {s} resposta/ação do sistema):

- {u} acessa a página de login;
- {s} renderiza a página/formulário de login;
- {u} insere os dados, login/senha;
- {u} submete o formulário;
- {s} recebe os dados através da requisição;
- {s} realiza a autenticação do usuário;
- {s} garante acesso do usuário ao sistema.

Esse é um fluxo bem comum que acontece todos os dias em qualquer etapa de login.

Agora que temos o caminho feliz, que diabos são exceções? Exceções nada mais são que ações/respostas que impedem o caminho feliz de prosseguir. Só nesses 7 passos, eu consigo encontrar de três a quatro exceções (depende do ponto de vista):
1º - Usuário não informa login;
2º - Usuário não informa a senha;
3º - O usuário ou a senha não conferem;
4º - Não há conexão com o SGBD para validar o login.

Tirando o quarto item, que é um evento não cometido pelo usuário, os demais são muito comuns de acontecer. Agora, vejamos um tratamento bem simples.

$usuario = $_POST['usuario'];
$senha = $_POST['senha'];

$autenticacao = new Autenticacao();//Nome irrelevante, apenas criado para demonstração.

try {
   $autenticacao->autenticar($usuario , $senha);   
} catch(Exception $e) {
   var_dump('Ocorreu algum problema: '.$e->getMessage());
}

Esse é um tratamento simples. Por algum motivo, não foi possível se autenticar e o usário é avisado o motivo.

Mas espera ai! Eu quero ter tratamentos diferentes para cada exceção. Como por exemplo:
1º - Avisar que o usuário, ou senha, não foi informado;
2º - Toda vez que o usuário errar a senha, salvar qual o usuário que a errou;
3º - Se houver problema de conexão, redirecionar o usuário para uma página de manutenção.

Agora sim, temos três tratamentos distintos, logo, três exceptions diferentes.

A SPL oferece algumas exceptions comumente utilizadas no sistema

Utilizarei, para exemplo, as seguintes:

- InvalidArgumentException para quando o usuário ou a senha não forem informados;
- RuntimeException para quando não for possível realizar a conexão com o SGBD;
- Criarei a exception InvalidLoginException, extendendo diretamento Exception, para quando o usuário e senha não forem encontrados no SGBD.

$usuario = $_POST['usuario'];
$senha = $_POST['senha'];

$autenticacao = new Autenticacao();//Nome irrelevante, apenas criado para demonstração.

try {
   $autenticacao->autenticar($usuario , $senha);   
} catch(InvalidArgumentException $e) {
   var_dump('Os campos usuário e senha não devem ser em brancos');
} catch(InvalidLoginException $e) {
   var_dump('Usuário ou senha inválidos, você não pode exceder cinco tentativas: ');
   $autenticacao->salvarUsuarioNaoAutenticado($usuario);
} catch(RuntimeException $e) {
   header('Location: http://meusite.com.br/manutencao.html');
}

 

1. Qual o objetivo de colocar outros nomes em uma Exception?


Acredito que agora ficou clara a resposta, que é ter tratamentos específicos e diferenciados para cada tipo de exceção.

2. Aproveitando o tópico qual a hora certa de aplicar: try e catch? e para que serve exatamente eles?

Responderei inversamente. try catch são estruturas de controle para tratar o "caminho feliz" e, se necessário, suas exceções. A partir do momento que ocorre uma exceção, o fluxo normal é interrompido e inicia-se o fluxo de tratamento da exceção.

Como mantra pessoal, e de alguns outros programadores, eu sempre uso exceptions sempre que eu tenho "vontade" de validar o retorno de alguma função e dar algum caminho alternativo dependendo do resultado. Entretanto, não é apenas isso, pois você deve entender quando uma função deve ou não ter um retorno.

Como regra, funções só devem ter retorno quando em seu nome for identificado por um prefixo, tais como:
- get*;//Retorna um valor qualquer (tradução "pegar")
- has*;//Retorna um boolean (tradução "tem")
- is*;//Retorna um boolean (tradução "é").

Como o meu método se chama "autenticar", eu não estou pedindo nenhuma resposta, apenas "ordenando" que realize a autenticação do usuário.

 

Como eu posso utilizar tratamento de exceções no método salvarUsuarioNaoAutenticado. Tais como:

- Não há conexão no SGBD;

- O usuário não existe na tabela de usuários, então não é necessários salvar;

- Entre outros.

 

É impossível ter um sistema a prova de exceções, mas muitas delas são de fácil identificação e resolução.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Muito obrigado pela ajuda de vocês :)

Só uma dúvida
Se renomearmos as Exception veja

throw new InvalidLoginException('Usuário ou senha inválidos, você não pode exceder cinco tentativas ');

Isso obrigará a criar essa classe estendendo a Exception certo? Então ficando assim (uma classe vazia, sem nenhum método e atributos):

class InvalidLoginException extends Exception {

}

Bom aproveitando o exemplo que o Gabriel passou(fiz algumas modificações)

class Autenticacao {
    public function autenticar($usuario, $senha) {
        /* não coloquei if para a verificação de usuario e senha, só para simplificar */
        throw new InvalidLoginException('Usuário ou senha inválidos, você não pode exceder cinco tentativas ');
    }
	
    public function salvarUsuarioNaoAutenticado($usuario) {
	echo '<br />';
        echo 'Usuario: ('.$usuario.' nao autenticado) salvo';
    }
}

class InvalidLoginException extends Exception { 

}

$usuario = 'Zezinho'; 
$senha = 'minha senha secreta'; 
$autenticacao = new Autenticacao();
 
try { 
    $autenticacao->autenticar($usuario , $senha); 
} catch(InvalidLoginException $e) { 
    echo $e->getMessage(); 
    $autenticacao->salvarUsuarioNaoAutenticado($usuario); 
}

Bom eu poderia colocar o método salvarUsuarioNaoAutenticado() que é da classe autenticacao na

classe InvalidLoginException?

 

Ficando assim

$usuario  = 'Zezinho';
$senha    = 'minha senha secreta';
$autenticacao = new Autenticacao();

try {
   $autenticacao->autenticar($usuario , $senha);   

} catch(InvalidLoginException $e) {
   echo $e->getMessage();
   $e->salvarUsuarioNaoAutenticado($usuario); // Agora esse método e da classe InvalidLoginException 
}


Resumindo como poderíamos aproveitar de uma maneira eficiente, essas classes filhas da Exception(para não ficar uma classe vazia, sem nenhum método e atributos)? Se possível poderiam dá um exemplo pratico?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Isso obrigará a criar essa classe estendendo a Exception certo? Então ficando assim (uma classe vazia, sem nenhum método e atributos)

Sim, exatamente. Você até pode utilizar mensagens personalizadas, entretanto, cada área que houver uma exception, o retorno pode ser diferente para o usuário.

Bom eu poderia colocar o método salvarUsuarioNaoAutenticado() que é da classe autenticacao na

classe InvalidLoginException?

 

Resumindo como poderíamos aproveitar de uma maneira eficiente, essas classes filhas da Exception(para não ficar uma classe vazia, sem nenhum método e atributos)? Se possível poderiam dá um exemplo pratico?

Não, a classe exception tem apenas uma responsabilidade, informar uma exceção.

 

A partir da exception, e não na exception, você deve tratar o novo fluxo (caminho).

Compartilhar este post


Link para o post
Compartilhar em outros sites

Se renomearmos as Exception veja

throw new InvalidLoginException('Usuário ou senha inválidos, você não pode exceder cinco tentativas ');

Isso obrigará a criar essa classe estendendo a Exception certo?

Sim. new InvalidLoginException repare que há um NEW, você está instanciando uma classe, portanto ela deve existir..não é apenas uma nomenclatura qualquer.

 

 

 

Então ficando assim (uma classe vazia, sem nenhum método e atributos):

class InvalidLoginException extends Exception {

}

Com certeza essa parte você não entendeu.. você está estendendo funcionalidades de OUTRA classe, talvez na sua filha não haja nada, porém na classe pai talvez sim.

 

A Exception possui uma estrutura parecida com essa:

class Exception {

    protected $message;
    protected $code;
    protected $file;
    protected $line;

    public function __construct($message = "", $code = 0, Exception $previous = null) { 

    }

    final public function getMessage() {

    }

    final public function getCode() { 

    }

    final public function getFile() {
        // ...
    }

    final public function getLine() {

    }

    final public function getTrace() {

    }

    final public function getPrevious() {

    }

    final public function getTraceAsString() {

    }

    public function __toString() {

    }

    final private function __clone() {

    }
}

Se necessário ela pode ser modificada quando estendida passando outros parâmetros para o construtor. Entenda, passar parâmetros relativos a exceção e não realizar algum tipo de lógica ou executar algum tipo de ação. E como você pode ver os parâmetros necessários para tratar a maioria das exceções já está na Exception.

 

Bom eu poderia colocar o método salvarUsuarioNaoAutenticado() que é da classe autenticacao na

classe InvalidLoginException?

Isso já foi respondido acima.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não, a classe exception tem apenas uma responsabilidade, informar uma exceção.

 

Eu discordo ligeiramente aqui. Criar uma classe estendendo a Exception ou alguma outra que dela derive só porque é possível ou só porque se pode ter try..catch() mais elaborados, pra mim, é bobeira.

 

Tudo bem que cada classe deve ter uma única responsabilidade mas as mensagens das exceções também são de responsabilidade das classes que as dispararam.

 

E por criar N métodos parametrizáveis nas classes filhas de Exception você separa a mensagem literal da lógica do código.

 

Por exemplo, uma das coisas que meu Roteador Padrão faz é pré-validar uma requisição em busca de parâmetros requeridos, ou seja, aqueles que devem, irrevogavelmente, estar presentes na URI recebida pela Aplicação estão de fato, presentes E com um valor válido, seja por expressão regular, seja por uma lista de valores aceitáveis.

 

Caso um ou mais parâmetros não estejam presentes ou sejam inválidos, eu lanço uma Exception informando o ocorrido. Poderia normalmente fazer da forma tradicional, mas veja, minhas classes Exceptions estendem minha própria superclasse que permite também que eu envie automaticamente um HTTP Response Code.

 

Hoje, eu tenho como mais adequado enviar um código 400 (Bad Request), mas posso estar enganado quanto a isso e, para consertar, teria que abrir o código do Roteador (que tem lá suas 500 linhas), procurar todas as ocorrências desse código 400 e substituir quando que se eu eu fizesse como faço atualmente, alteraria uma única vez:

 

RouterException.php

class RouterException extends \Next\Components\Debug\Exception {

    // ...

    /**
     * Invalid Required Parameter
     *
     * A required Route Parameter has an invalid value
     *
     * @param string $parameter
     *   Route Parameter being analyzed
     *
     * @return Next\Controller\Router\RouterException
     *   Exception for invalid required parameter
     */
    public static function invalidParameter( $parameter ) {

        return new self(

            'Invalid Required Parameter <strong>%s</strong>',

            self::INVALID_REQUIRED_PARAM,

            $parameter,

            // Bad Request!

            400
        );
    }
}

Router\Standard.php

throw RouterException::invalidParameter( 'parameter' );

Sem contar que, com a limitação de 80 caracteres por linha da PSR-2 (eu ignoro o máximo possível o hard limit de 120), para classes de Exception com nomes muito grandes (sempre tem uma) cada caractere "economizado" (new) é bem vindo. :closedeyes:

Compartilhar este post


Link para o post
Compartilhar em outros sites

Concordo com a explanação, além de que, sua classe ainda está informando exceções.

 

Mas ao que me referia era a responsabilidade do método salvarUsuarioNaoAutenticado, e nisso seria outra responsabilidade, fora do escopo da exception.



meu limite por linha também é 120.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Imagino que por ele estar aprendendo tenha se equivocado quanto a dar um echo dentro do método da Exception, daí sim até eu concordo.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.