Exceptions: re-throw and multiple catch blocks - php

I have a situation where I would like to re-throw an exception inside a catch block and have it caught by a more generic catch.
Example:
try {
somethingThatMightThrowExceptions();
}
catch (ClientErrorResponseException $e) {
if ($e->getResponse()->getStatusCode() == 404) {
return $this->notFound();
}
throw $e; // even tried throw new \Exception();
}
catch (\Exception $e) {
return $this->serverError('Server error');
}
So in my first catch block I check for a specific condition, and if that fails I would like to re-throw the exception and have it caught by the generic catch (\Exception) block. But in this case it just bubbles back up to the caller.
The problem is that there are actually a couple more lines of code in the final catch, which I don't want to repeat. I could of course extract them to a method, but that feels like overkill.
So basically I want to do it all in-line without having to add extra layers. Any suggestions?

It's due to catch only working once as you are throwing an exception not withing a try block.
If you are determined, that you want to do it this way, then you need to nest try..catch statements as #deceze suggested in comments..
You should describe what you are trying to accomplish instead, as there might be a better way to do it.

You have two options:
a) Extract common logic to a method. (This can be overkill sometimes as you've mentioned)
b) Catch the most generic exception and check for its type. ie:
try{
somethingThatMightThrowExceptions();
}
catch (\Exception $e) { // The most generic exception
if($e instanceof ClientErrorResponseException) {
// Some actions specific to ClientErrorResponseException
}
// Shared actions
}

You have two options:
a) Catch the most generic exception and check for its type, as #deceze suggested
try{
somethingThatMightThrowExceptions();
}
catch (\Exception $e) { // The most generic exception
if($e instanceof ClientErrorResponseException) {
// Some actions specific to ClientErrorResponseException
}
// Shared actions
}
b) Put anoter try/catch block at upper level
try {
try {
somethingThatMightThrowExceptions();
}
catch (ClientErrorResponseException $e) {
// Some actions specific to ClientErrorResponseException
throw $e ;
}
}
catch (\Exception $e) { // The most generic exception
// Shared actions
}

Related

Handling Multiple Exception Types For One Try Block

Is there a cleaner way to handle multiple exception types for a single try block? As you will see below we have specific code that is to be executed for the different catch statements.
This function is one of many which use the repeated try catch logic. Is it possible to eliminate this repeated catch code somehow?
public function add_payment_method(Request $request){
try{
// Try to do some stuff
} catch (\Stripe\Error\RateLimit $e) {
/*
CODE FOR THIS SPECIFIC EXCEPTION
*/
} catch (\Stripe\Error\InvalidRequest $e) {
/*
CODE FOR THIS SPECIFIC EXCEPTION
*/
} catch (\Stripe\Error\Authentication $e) {
/*
CODE FOR THIS SPECIFIC EXCEPTION
*/
} catch (\Stripe\Error\Base $e) {
/*
CODE FOR THIS SPECIFIC EXCEPTION
*/
} catch (\Stripe\Error\ApiConnection $e) {
/*
CODE FOR THIS SPECIFIC EXCEPTION
*/
} catch (\Exception $e) {
/*
CODE FOR GENERIC EXCEPTION
*/
}
}
All of these errors extend Stripe\Error\Base, so catching Stripe\Error\Base should cover the more specific ones.
That said, Stripe provides different exception types because you should be doing different actions in response to the different types. A RateLimit exception is something you should queue and retry later. A Card exception is something you should kick back to the user to fix. An Authentication error might mean your entire app is unable to take credit cards because your API token is missing/wrong and you're losing $$$.
See Catching multiple exception types in one catch block
Apart from a thousand ifelse, one can do
catch (Exception $e) {
switch ($e) {
case $e instanceof Exception1:
...;
break;
case $e instanceof Exception2:
...;
break;
default:
...;
}
}

PHP exception inside catch: how to handle it?

Suppose to have a PHP code inside a try...catch block. Suppose that inside catch you would like to do something (i.e. sending email) that could potentially fail and throw a new exception.
try {
// something bad happens
throw new Exception('Exception 1');
}
catch(Exception $e) {
// something bad happens also here
throw new Exception('Exception 2');
}
What is the correct (best) way to handle exceptions inside catch block?
Based on this answer, it seems to be perfectly valid to nest try/catch blocks, like this:
try {
// Dangerous operation
} catch (Exception $e) {
try {
// Send notification email of failure
} catch (Exception $e) {
// Ouch, email failed too
}
}
You should not throw anything in catch. If you do so, than you can omit this inner layer of try-catch and catch exception in outer layer of try-catch and process that exception there.
for example:
try {
function(){
try {
function(){
try {
function (){}
} catch {
throw new Exception("newInner");
}
}
} catch {
throw new Exception("new");
}
}
} catch (Exception $e) {
echo $e;
}
can be replaced to
try {
function(){
function(){
function (){
throw new Exception("newInner");
}
}
}
} catch (Exception $e) {
echo $e;
}
You have 2 possible ways:
You exit the program (if it is severe) and you write it to a log file and inform the user.
If the error is specifically from your current class/function,
you throw another error, inside the catch block.
You can use finally. Code in this branch will be executed even if exception is thrown within catch branch

Php selective exception handling

I have a problem where I want to catch all exception except descendants of my custom exception.
Maybe bad design, but here it is (Simplified and names changed, but the code is quite accurate):
function doStuff()
{
try {
// code
if (something) {
// manually throw an exception
throw StuffError("Something is bad.");
}
// a third-party code, can throw exceptions
LibraryClass::arcaneMagic();
} catch (Exception $e) {
throw new StuffError("Error occured while doing stuff: "
. $e->getMessage());
}
}
/** My custom exception */
class StuffError extends Exception
{
function __construct($msg) {
parent::__construct('StuffError: ' . $msg);
}
}
However, the issue here is that I don't want the try-catch to intercept the manually throws StuffError. Or, seamlessly rethrow it or something.
As it is now, I'd get:
StuffError: Error occured while doing stuff: StuffError: Something is bad.
I want just:
StuffError: Something is bad.
How would I do it?
You can have multiple catch clauses, and the first one that matches will be the one that runs. So you could have something like this:
try {
do_some_stuff();
}
catch (StuffError $e) {
throw $e;
}
catch (Exception $e) {
throw new StuffError(Error occurred while doing stuff: " . $e->getMessage());
}
But you might want to rethink wrapping stuff like this. It obscures the real cause of the error. For one thing, you lose the stack trace. But it also complicates error handling, since now someone can't differentiate exception types the way you're trying to do, short of trying to parse the exception message (which is rather an anti-pattern in itself).
I might be misinterpreting you, but I think this is what you're looking for:
...
} catch (Exception $e) {
if (get_class($e) == 'StuffError' || is_subclass_of($e, 'StuffError')) {
throw $e;
} else {
throw new StuffError("Error occured while doing stuff: "
. $e->getMessage());
}
}
...
Replace your catch statement with the code above. It checks to see if the exception is a StuffError or a child class of StuffError. I'm still very confused at why you would need to throw a StuffError exception after you catch, but maybe that's just some weirdness coming from translating/cleaning your code.

Too many try/catch block for PDO

In the controllers class files, most of the method functions include try/catch block something like this:
try
{
$stmt = $this->prepare($sql);
$stmt->execute($params);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
//foreach() or so on...
}
catch (Exception $e)
{
//bunch of code...
//save error into database, etc.
//error into json and pass to view file
}
There are a lot of code in the catch block, is there a way to reduce it. Is possible to add "throw exception" in the catch block?
Yes, it is. Try it by yourself. You can always throw a new Exception in a catch block or rethrow the same exception.
try
{
// ...
}
catch (Exception $e)
{
// do whatever you want
throw new Your_Exception($e->getMessage());
// or
throw $e;
}
I don't know what "bunch of code" is. I'm not sure I believe you. If you have that much going on in a catch block you're doing something wrong.
I'd put this kind of code into an aspect if you have AOP available to you.
"Error into database" might throw its own exception. What happens to that?
The only step that I see here that's necessary is routing to the error view.
What does rethrowing the exception do? It's just passing the buck somewhere else. If all these steps don't need to be done, and all you're doing to rethrowing, then don't catch it at all. Let the exception bubble up to where it's truly handled.
You shouldn't be catching Exception. That's much too general. Catch each specific type of Exception with multiple catch statements on your try block:
try {
} catch(PDOException $err) {
} catch(DomainException $err) {
}

How to pass the exception caught in inner catch to outer catch in a nested try catch

I am nesting try catches inside of a main try catch statement, what I would like to know is how I can make the main try catch fail if one of the nested try catches fails?
Here is my code:
try
{
try
{
//how can I make the main try catch fail if this try catch fails?
}
catch(Exception $e)
{
error_log();
}
}
catch(Exception $e)
{
error_log();
}
After error_log(); in the first try-catch, type throw $e; (on a new line). This will throw the error again, and the outer try-catch will handle it.
You should extend Exception for the various different types of Exception. That way you can trigger a specific try-catch block:
try
{
...
try
{
throwSomeException();
}
catch ( InnerException $e )
{
...do stuff only for InnerException...
}
...
}
catch ( Exception $e )
{
...do stuff for all types of exception...
}
Additionally, you can chain your catch statements to trigger different blocks in a single try-catch:
try
{
...
}
catch ( SpecificTypeOfException $e )
{
..do something specific
}
catch ( TypeOfException $e )
{
..do something less specific
}
catch ( Exception $e )
{
..do something for all exceptions
}
Inside the inner catch, throw() - NOT recommended, I've seen several issues with PHP when doing this. Or set a flag to throw just after the inner catch.
Here's an example throwing the same exception (or you could throw a different one).
try {
$ex = null;
try {
//how can I make the main try catch fail if this try catch fails?
} catch(Exception $e){
$ex = $e;
error_log();
}
if ($ex) {
throw $ex;
}
} catch(Exception $e){
error_log();
}
I handle exceptions in a way similar to eventHandling in Javascript.
An event bubbles up the ladder from specific to generic. When it reaches the start program, an exception lost all it's meaning to the code and should simply be caught for logging and ending an application.
In the meantime a lot of stuff can happen
CallStack:
Start Lunch
Eat Apple (Before this code, an apple was bought as lunch)
Sink teeth in apple
During my eating of the apple, a worm appeared:
throw NausiaException('I found a bleeding worm...');
Eat Apple scope catches
catch(Exception $e)
the exception because in that scope we can return the apple to the store and shout at the manager. Since nothing more useful can be said about the occurrence,
throw $e
is called because eating the apple failed.
Something could've gone different
However, if the store manager refused to refund, you can wrap the exception
throw new RefundFailedException('The manager is a cheap skate', RefundFailedException::REFUSED, $e)
Start lunch Scope
Start lunch scope wants to throw away bad lunch
try {
//Start lunch
} catch (Exception $e) {
switch (true) {
case $e instanceof NausiaException:
case $e instanceof RefundFailedException:
//Throw lunch away
break;
}
}
use of a bool variable and "return" keyword at appropriate place could do the trick for you...

Categories