Viewed   84 times

I'd like a cleaner way to obtain the following functionality, to catch AError and BError in one block:

try
{
    /* something */
}
catch( AError, BError $e )
{
    handler1( $e )
}
catch( Exception $e )
{
    handler2( $e )
}

Is there any way to do this? Or do I have to catch them separately?

AError and Berror have a shared base class, but they also share it with other types that I'd like to fall through to handler2, so I can't just catch the base class.

 Answers

3

Update:

As of PHP 7.1, this is available.

The syntax is:

try
{
    // Some code...
}
catch(AError | BError $e)
{
    // Handle exceptions
}
catch(Exception $e)
{
    // Handle the general case
}

Docs: https://www.php.net/manual/en/language.exceptions.php#example-294

RFC: https://wiki.php.net/rfc/multiple-catch

Commit: https://github.com/php/php-src/commit/0aed2cc2a440e7be17552cc669d71fdd24d1204a


For PHP before 7.1:

Despite what these other answers say, you can catch AError and BError in the same block (it is somewhat easier if you are the one defining the exceptions). Even given that there are exceptions you want to "fall through", you should still be able to define a hierarchy to match your needs.

abstract class MyExceptions extends Exception {}

abstract class LetterError extends MyExceptions {}

class AError extends LetterError {}

class BError extends LetterError {}

Then:

catch(LetterError $e){
    //voodoo
}

As you can see here and here, even the SPL default exceptions have a hierarchy you can leverage. Additionally, as stated in the PHP Manual:

When an exception is thrown, code following the statement will not be executed, and PHP will attempt to find the first matching catch block.

This means you could also have

class CError extends LetterError {}

which you need to handle differently than AError or BError, so your catch statement would look like this:

catch(CError $e){
    //voodoo
}
catch(LetterError $e){
    //voodoo
}

If you had the case where there were twenty or more exceptions that legitimately belonged under the same superclass, and you needed to handle five (or whatever large-ish group) of them one way and the rest the other, you can STILL do this.

interface Group1 {}

class AError extends LetterError implements Group1 {}

class BError extends LetterError implements Group1 {}

And then:

catch (Group1 $e) {}

Using OOP when it comes to exceptions is very powerful. Using things like get_class or instanceof are hacks, and should be avoided if possible.

Another solution I would like to add is putting the exception handling functionality in its own method.

You could have

function handleExceptionMethod1(Exception $e)
{
    //voodoo
}

function handleExceptionMethod2(Exception $e)
{
    //voodoo
}

Assuming there is absolutely no way you can control exception class hierarchies or interfaces (and there almost always will be a way), you can do the following:

try
{
    stuff()
}
catch(ExceptionA $e)
{
    $this->handleExceptionMethod1($e);
}
catch(ExceptionB $e)
{
    $this->handleExceptionMethod1($e);
}
catch(ExceptionC $e)
{
    $this->handleExceptionMethod1($e);
}
catch(Exception $e)
{
    $this->handleExceptionMethod2($e);
}

In this way, you are still have a only single code location you have to modify if your exception handling mechanism needs to change, and you are working within the general constructs of OOP.

Saturday, September 17, 2022
1

Exceptions allow you to distinguish between different types of errors, and is also great for routing. For example...

class Application
{
    public function run()
    {
        try {
            // Start her up!!
        } catch (Exception $e) {
            // If Ajax request, send back status and message
            if ($this->getRequest()->isAjax()) {
                return Application_Json::encode(array(
                    'status' => 'error',
                    'msg'    => $e->getMessage());
            }

            // ...otherwise, just throw error
            throw $e;
        }
    }
}

The thrown exception can then be handled by a custom error handler.

Since PHP is a loosely typed language, you might need to ensure that only strings are passed as arguments to a class method. For example...

class StringsOnly
{
    public function onlyPassStringToThisMethod($string)
    {
        if (!is_string($string)) {
            throw new InvalidArgumentException('$string is definitely not a string');
        }

        // Cool string manipulation...

        return $this;
    }
}

...or if you need to handle different types of exceptions in different ways.

class DifferentExceptionsForDifferentFolks
{
    public function catchMeIfYouCan()
    {
        try {
            $this->flyForFree();
        } catch (CantFlyForFreeException $e) {
            $this->alertAuthorities();
            return 'Sorry, you can't fly for free dude. It just don't work that way!';
        } catch (DbException $e) {
            // Get DB debug info
            $this->logDbDebugInfo();
            return 'Could not access database. What did you mess up this time?';
        } catch (Exception $e) {
            $this->logMiscException($e);
            return 'I catch all exceptions for which you did not account!';
        }
    }
}

If using transactions in something like Zend Framework:

class CreditCardController extends Zend_Controller_Action
{
    public function buyforgirlfriendAction()
    {
        try {
            $this->getDb()->beginTransaction();

            $this->insertGift($giftName, $giftPrice, $giftWowFactor);

            $this->getDb()->commit();
        } catch (Exception $e) {
            // Error encountered, rollback changes
            $this->getDb()->rollBack();

            // Re-throw exception, allow ErrorController forward
            throw $e;
        }
    }
}
Tuesday, September 13, 2022
 
alexn
 
1

Web programming is no different than desktop programming in this regard.

Use exceptions for exceptional events. Use return values for anything expected.

I think seeing an empty query result isn't something that will have an exception thrown, generally. I'd just return no data. If the database query failed somehow, that would be a reason to throw an exception. Also, your exception messages should probably be more descriptive, rather than "you need to fix this".

Finally, in PHP 5.3, you can set an inner exception by passing it as the third parameter in the Exception constructor.

http://www.php.net/manual/en/class.exception.php

Saturday, October 22, 2022
 
trante
 
5
catch (...)
{
   // Handle exceptions not covered.
}

Important considerations:

  • A better approach is to catch specific types of exception that you can actually recover from as opposed to all possible exceptions.
  • catch(...) will also catch certain serious system level exceptions (varies depending on compiler) that you are not going to be able to recover reliably from. Catching them in this way and then swallowing them and continuing could cause further serious problems in your program.
  • Depending on your context it can be acceptable to use catch(...), providing the exception is re-thrown. In this case, you log all useful local state information and then re-throw the exception to allow it to propagate up. However you should read up on the RAII pattern if you choose this route.
Tuesday, August 2, 2022
 
mosg
 
2

No. That's how one should do it. The throw myException() can only occur if the first exception has been caught and hence is no longer 'in flight'.

This design pattern is quite common to 'translate' error messages coming from another library that your code is using to an error that the user of your code can better relate to.

Alternatively, if you want to do more than merely throw (say you want to do some clearing up of resources -- though that should really be done via RAII, i.e. from destructors), then you can simply rethrow the original exception via

try
{
    // ... code that may throw
}
catch(...) // catches anything
{
    // ... code that runs before rethrowing
    throw;    // rethrows the original catch
}
Sunday, August 7, 2022
 
maqjav
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :