The purpose is for you to be able to catch a specific exception.
All Exceptions in PHP are derived from the Exception
class. When you capture Exception
you are capturing any exception that occurs while executing the code.
Example:
try{
throw new InvalidArgumentException('Teste');
} catch (Exception $e) {
echo $e->getMessage();
}
In this case, it is advantageous to do so if you really want to catch any exception that occurs in that portion of Try/catch
.
Why use a specific exception?
But that is not always the case. Often, you need to know exactly what was the exception that occurred to perform a particular error handling.
For example, if a function invokes a mathematical miscalculation exception and another points that the argument is not numeric, you may want to only use the exception, either it tells you the calculation error to inform a user log, whereas the you may want to leave it as a fatal error (because in PHP, when you throw an uncaught exception, you generate a fatal error).
Capturing Specific Exceptions
One point we have to make is that the Exceptions are varied because the problems are varied. So you use a specific Exception for each problem.
Consider for example a function where you have an argument that must be a array
and that array
must have 2 items. In this case, you will have to use an exception to indicate that it is not an array, and another to indicate an invalid size.
Example:
function tem_que_ser_array_com_dois_itens($array) {
if (! is_array($array)) {
throw new \InvalidArgumentException('O argumento não é array');
}
if (count($array) !== 2) {
throw new \LengthException("Tamanho do array é inválido. Deve ser apenas 2 itens');
}
}
So, to work with the possible exceptions thrown by this function, you can specify which exception you want to "catch" in catch if you did not want to catch any.
Example:
try {
tem_que_ser_um_array_com_dois_itens(1);
} catch (InvalidArgumentException $e) {
$e->getMessage();
}
In the example above, only InvalidArgumentException
is captured, because we want to treat an exception only if the argument is not valid. If the size is invalid, in the example above, the exception will be converted by PHP to a Fatal error, since it was not "caught".
If you used Exception
in catch
above, you would catch any exception generated by the function - Because all exception classes are derived from Exception
, in PHP versions prior to version 7.
Multiple exception captures
However, this behavior may not be what you want. Then, you may want to handle both invalid argument and invalid argument exceptions. How could I solve this?
The solution is to use multiple catch
. PHP allows this.
Example:
try {
tem_que_ser_array_com_dois_itens([1]);
} catch (InvalidArgumentException $e) {
echo 'Por favor, coloque um array';
} catch (LengthException $e) {
echo 'Seu array tá com tamanho errado';
}
Responding simply to your questions:
But what is the purpose of having so many exceptions like this?
Each Exception represents an exception that may occur during the code. Exceptions can be invalid sizes, unexpected types, runtime errors. So there needs to be a specific Exception for each case, because if you used throw new Exception
for everything, you would not know how to handle a specific exception and it would give you a lot of headaches.
In addition to using the PHP exceptions themselves, you can also create your own.
Example:
class InvalidImageExtension extends \InvalidArgumentException {}
if (! in_array($extensao, ['png', 'jpg', 'bmp']) {
throw new InvalidImageExtension('Extensão inválida');
}
Is there a difference between using one and the other?
As I said, you need an Exception to identify a specific problem. That is, it would not be arranged to throw the same exception to handle invalid argument and to handle an error when opening a file. Each case is a case.
Example that would not be legal:
try {
if (is_array($array) {
// Poderia ser UnexpectedValueException
// Ou InvalidArgumentException, se isso for argumento de uma função
throw new Exception('Não é um array');
}
if (file_exists($file)) {
// Poderia ser RunTimeException (erro no tempo de excecução)
throw new Exception('Arquivo não existe');
}
} catch (Exception $e) {
// Se seu chefe pedir para você enviar um e-mail
// Toda vez que não conseguir abrir um arquivo
// Como você ia tratar isso? Fazendo IF?
// Não seria melhor lançar e capturar uma exceção só para o arquivo não existente?
}
Show which Exception your function throws
I do not know if you usually use PHPDocumentor
, but it's always good to note that some libraries specify exceptions that can be thrown in your code by comments like @throws
. So, if you wish, you can capture the specific exception, rather than trying to guess.
Example:
/**
*
* @param boolean $motor_ligado
* @param string $modelo
* @throws ProblemaNoMotorException
* @throws VeiculoNaoVoadorException
*/
function voar($motor_ligado = true, $modelo = 'aviao')
{
if ($motor_ligado === false) {
throw new ProblemaNoMotorException('Motor não está ligado');
}
if (! in_array($modelo, ['aviao', 'helicoptero']) {
throw new VeiculoNaoVoadorException('Esse veículo não serve');
// Ou UnexpectedValueException, por exemplo
}
}
What is rethrow?
I've seen this term being used in some frameworks, rethrow
. In other words, "cast again". Sometimes you want to do a specific operation on catch
, but you still need the Fatal Error
generated by that exception.
In this case, you can throw the same exception after capturing it in catch
.
See a useful example where I need to finalize a request in a client's webservice. If an error occurs, in addition to throwing the exception, I need to log it in a log.
So I can capture it, log it and launch it again:
try {
$db->finalizarNoWebserviceDoCliente();
} catch (ServerException $e) {
$db->logs()->registrarLog($e->getMessage());
// Você lança a exceção novamente aqui
throw $e;
}