Exceções
Índice
O PHP possui um modelo de exceções similar ao de outras linguagens
de programação. Uma exceção pode ser lançada (throw
) e capturada (catch
).
Código pode ser envolvido por um bloco try
para facilitar a captura
de exceções potenciais. Cada bloco try
precisa ter ao menos um bloco
catch
ou finally
correspondente.
Se uma exceção é lançada e o escopo atual não possui nenhum block catch
,
a exceção irá "desempilhar" o call stack pelas funções chamadoras
até encontrar um bloco catch
compatível. Todos os blocks finally
encontrados
pelo caminho serão executados. Se a pilha é esvaziada até o
escopo global sem encontrar um block catch
compatível, o programa irá
terminar com um erro fatal a não ser que um manipulador global de exceções tenha sido configurado.
O objeto lançado precisa ser uma instanceof
Throwable.
Tentar lançar um objeto sem essa ascendência resultará em um erro fatal.
A partir doPHP 8.0.0, a palavra chave throw
é uma expressão e pode ser utilizada em qualquer
contexto de expressão. Em versões anteriores era considerado uma instrução e portanto precisava constar em sua própria linha.
catch
Um bloco catch
define como o código responde a uma exceção lançada. Um bloco catch
define um ou mais tipos de exceções ou erros que ele pode processar, e
opcionalmente uma variável para receber a exceção lançada. (A variável era
requerida anteriormente ao PHP 8.0.0.) O primeiro bloco catch
que uma exceção
ou erro lançados encontram que seja compatível com o tipo objeto lançado irá
processar esse objeto.
Múltiplos blocos catch
podem ser utilizados para capturar exceções
diferentes. A execução normal (quando nenhuma exceção é lançada dentro de um
try
) irão continuar a execução após o último catch
definido em sequência.
Exceções podem ser lançadas ou relançadas (throw
) dentro um bloco catch
. Caso contrário,
a execução irá continuar após o blococatch
que foi acionado.
Quando uma exceção é lançada o código seguinte não é executado,
e o PHP tentará encontrar o primeiro bloco catch
coincidente.
Se uma exceção não for capturada, um erro PHP fatal será lançado com a mensagem
"Uncaught Exception ...
" na ausência de uma função
definida com set_exception_handler().
A partir do PHP 7.1 um bloco catch
pode especificar múltiplas exceções
usando o caractere pipe (|
). Isto é útil quando
diferentes exceções de diferentes hierarquias de classes são tratadas
da mesma forma.
A partir do PHP 8.0.0, o nome de variável que captura a exceção é opcional.
Se não especificada, o bloco catch
compatível ainda executará, mas não
terá acesso ao objeto lançado.
finally
Um bloco finally
pode ser especificado após
ou no lugar de blocos catch
. Códigos dentro de finally
sempre serão
executados depois do try
ou catch
, independente se houve o
lançamento de uma exceção, e antes que a execução normal continue.
Uma interação notável ocorre entre um bloco finally
e a instrução return
.
Se uma instrução return
é encontrada dentro um bloco try
ou catch
,
o bloco finally
ainda assim será executado. Além disso a instrução return
é avaliada
no ponto que é encontrada, mas o resultado só será retornado após o bloco finally
ser executado. Se o bloco finally
também tiver uma instrução return
,
o valor da instrução de finally
que será retornado.
Manipulador de exceções global
Se uma exceção alcança o escopo global, ela ainda pode ser capturada
por um manipulador global de exceções, opcional. A função set_exception_handler()
pode configurar uma função que será chamada no lugar de um bloco catch
na ausẽncia
de outros blocos. O efeito é essencialmente o mesmo que ter o programa inteiro
envelopado dentro de um bloco try
-catch
e a função informada funcionando como o catch
.
Notas
Nota:
Funções internas ao PHP utilizam principalmente
aviso de erros. Apenas extensões
orientadas a objetos
utilizam exceções. No entanto os erros podem ser transformados em
exceções com ErrorException.
Essa técnica não funciona para erros fatais.
Exemplo #1 Convertendo avisos de erros em exceções
<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
}
set_error_handler('exceptions_error_handler');
?>
Exemplos
Exemplo #2 Lançando uma exceção
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Divisão por zero.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Exceção capturada: ', $e->getMessage(), "\n";
}
// Execução continua
echo "Olá mundo\n";
?>
O exemplo acima produzirá:
0.2
Exceção capturada: Divisão por zero.
Olá mundo
Exemplo #3 Manipulação de exceções com bloco finally
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Divisão por zero.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
} catch (Exception $e) {
echo 'Exceção capturada: ', $e->getMessage(), "\n";
} finally {
echo "Primeiro finaly.\n";
}
try {
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Exceção capturada: ', $e->getMessage(), "\n";
} finally {
echo "Segundo finally.\n";
}
// Execução continua
echo "Olá mundo\n";
?>
O exemplo acima produzirá:
0.2
Primeiro finally.
Exceção capturada: Divisão por zero.
Segundo finally.
Olá mundo
Exemplo #4 Interação entre blocos finally
e return
<?php
function test() {
try {
throw new Exception('foo');
} catch (Exception $e) {
return 'catch';
} finally {
return 'finally';
}
}
echo test();
?>
O exemplo acima produzirá:
Exemplo #5 Exceções alinhadas
<?php
class MyException extends Exception { }
class Test {
public function testing() {
try {
try {
throw new MyException('foo!');
} catch (MyException $e) {
// rethrow it
throw $e;
}
} catch (Exception $e) {
var_dump($e->getMessage());
}
}
}
$foo = new Test;
$foo->testing();
?>
O exemplo acima produzirá:
Exemplo #6 Manipulação de múltiplas exceções no mesmo bloco catch
<?php
class MyException extends Exception { }
class MyOtherException extends Exception { }
class Test {
public function testing() {
try {
throw new MyException();
} catch (MyException | MyOtherException $e) {
var_dump(get_class($e));
}
}
}
$foo = new Test;
$foo->testing();
?>
O exemplo acima produzirá:
Exemplo #7 Omissão da variável de captura de exceções
Permitido a partir do PHP 8.0.0.
<?php
class SpecificException extends Exception {}
function test() {
throw new SpecificException('Oopsie');
}
try {
test();
} catch (SpecificException) {
print "Uma exceção SpecificException foi lançada, mas os detalhes não importam.";
}
?>
Exemplo #8 Throw as an expression
Only permitted in PHP 8.0.0 and later.
<?php
function test() {
do_something_risky() or throw new Exception('It did not work');
}
try {
test();
} catch (Exception $e) {
print $e->getMessage();
}
?>