92

Is it possible to catch exception and continue execution of script?

Dominic Rodger
  • 97,747
  • 36
  • 197
  • 212
Kirzilla
  • 16,368
  • 26
  • 84
  • 129

9 Answers9

144

Yes but it depends what you want to execute:

E.g.

try {
   a();
   b();
}
catch(Throwable $ignored){
}

c();

c() will always be executed. But if a() throws an exception, b() is not executed.

Only put the stuff in to the try block that is depended on each other. E.g. b depends on some result of a it makes no sense to put b after the try-catch block.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 2
    `$e` needs to be `\Exception $e` or simular or a parse error will be thrown – iautomation Mar 01 '16 at 21:29
  • Straightforward explanation. It should be emphasized again that it's important to do something meaningful, in most cases, inside the `catch`. Otherwise the code just breaks and it might be hard to discern why. – MQuiggGeorgia Sep 29 '17 at 16:45
  • c() will be executed if there is now throw inside catch block, otherwise the code will be stopped – hendra1 Dec 25 '18 at 05:59
  • @Felix Kling, what if instead of doing just nothing log the error message? Will it still break the operation? – Ahmad Baktash Hayeri May 01 '20 at 17:15
117

Sure, just catch the exception where you want to continue execution...

try
{
    SomeOperation();
}
catch (SomeException $ignored)
{
    // do nothing... php will ignore and continue
    // but maybe use "ignored" as name to silence IDE warnings.  
}

Of course this has the problem of silently dropping what could be a very important error. SomeOperation() may fail causing other subtle, difficult to figure out problems, but you would never know if you silently drop the exception.

Top-Master
  • 7,611
  • 5
  • 39
  • 71
Doug T.
  • 64,223
  • 27
  • 138
  • 202
  • 7
    If i might add: catching an exception without doing anything in the catch block is considered bad style, you should at least write some log output (or, as in the example, provide a comment that _really, really, really_ nothing has to be done). This is especially true if you catch _any_ exception with catch(Exception $ex) {} –  Jan 25 '10 at 14:13
  • 3
    so how do you log it and still continue ? – numerical25 Feb 11 '13 at 14:18
  • 2
    Best pratice is to use: Try -> Catch (handle Exception) -> Finally (where finally always runs) maybe im wrong with your thoughts but none the less wanted to give you some input. – Bram B May 25 '16 at 15:01
  • Is there a built-in way (or convention) for explicitly saying "do nothing" like e.g. `pass` in Python? – cprn Oct 12 '16 at 18:52
  • Important: if you put in your code a line: [condition or die("whatever")] the try-catch doesnt work, die kill the program inmediatly. It has logic... after you debug the code – Grenville Tryon Aug 20 '18 at 21:55
  • The `try... catch... finally` block can be much more complex than it seems, even for such a simple purpose. It can have multiple _catches_ for specific exceptions, it can encapsulate other `try... catch... finally` blocks as needed, an exception can be re-thrown and changed from inside the `catch` or `finally` block and the specific behavior of the block is highly dependable on what is set by `error_reporting()` and if a handler has been defined with `set_exception_handler()`. I'd suggest a visit to the exception documentation at http://php.net/manual/en/language.exceptions.php to learn more. – Julio Marchi Oct 09 '18 at 16:05
22

Sure:

try {
   throw new Exception('Something bad');
} catch (Exception $e) {
    // Do nothing
}

You might want to go have a read of the PHP documentation on Exceptions.

Dominic Rodger
  • 97,747
  • 36
  • 197
  • 212
14

php > 7

use the new interface Throwable

    try {
        // Code that may throw an Exception or Error.
    } catch (Throwable $t) {
        // Handle exception
    }

echo "Script is still running..."; // this script will be executed.
Abdes
  • 926
  • 1
  • 15
  • 27
  • 8
    If you tell someone to "use" something, tell also "why". Why is `Throwable` better than `Exception`? – Daniel W. Jul 14 '20 at 19:35
  • This saved my bacon - I was calling a function of a class required by composer, which was in turn calling another function and not catching if that function produced an error - so no matter what, there was a gap in the try catch chain. I changed my code to Throwable and it caught it regardless, when for some reason \Exception wouldn't catch. – Lewis Feb 05 '21 at 12:49
  • Exception was probably caught, and what is known as swallowed elsewhere. Like this example. This means anything downstream of this won't know about the failure. Throwable is one level down so older code that does this might not swallow everything. It can be useful as a pattern known as bulkheading, but it is also customary to include a logger dependency and output some form of useful message about the failure and if you will recover or not. – MrMesees Nov 05 '21 at 05:48
  • @DanielW. because Exception implements Throwable; in php >7 if an Error is thrown Exception in the catch block wont catch it but Throwable will – ddruganov Nov 11 '21 at 07:04
8

For PHP 8+ we can omit the variable name for a caught exception.

catch

As of PHP 8.0.0, the variable name for a caught exception is optional. If not specified, the catch block will still execute but will not have access to the thrown object.

And thus we can do it like this:

try {
  throw new Exception("An error");
}
catch (Exception) {}
Christos Lytras
  • 36,310
  • 4
  • 80
  • 113
7

Yes.

try {
    Somecode();
catch (Exception $e) {
    // handle or ignore exception here. 
}

however note that php also has error codes separate from exceptions, a legacy holdover from before php had oop primitives. Most library builtins still raise error codes, not exceptions. To ignore an error code call the function prefixed with @:

@myfunction();
Wouter J
  • 41,455
  • 15
  • 107
  • 112
Crast
  • 15,996
  • 5
  • 45
  • 53
1

Another angle on this is returning an Exception, NOT throwing one, from the processing code.

I needed to do this with a templating framework I'm writing. If the user attempts to access a property that doesn't exist on the data, I return the error from deep within the processing function, rather than throwing it.

Then, in the calling code, I can decide whether to throw this returned error, causing the try() to catch(), or just continue:

// process the template
    try
    {
        // this function will pass back a value, or a TemplateExecption if invalid
            $result = $this->process($value);

        // if the result is an error, choose what to do with it
            if($result instanceof TemplateExecption)
            {
                if(DEBUGGING == TRUE)
                {
                    throw($result); // throw the original error
                }
                else
                {
                    $result = NULL; // ignore the error
                }
            }
    }

// catch TemplateExceptions
    catch(TemplateException $e)
    {
        // handle template exceptions
    }

// catch normal PHP Exceptions
    catch(Exception $e)
    {
        // handle normal exceptions
    }

// if we get here, $result was valid, or ignored
    return $result;

The result of this is I still get the context of the original error, even though it was thrown at the top.

Another option might be to return a custom NullObject or a UnknownProperty object and compare against that before deciding to trip the catch(), but as you can re-throw errors anyway, and if you're fully in control of the overall structure, I think this is a neat way round the issue of not being able to continue try/catches.

davestewart
  • 705
  • 1
  • 8
  • 11
0

An old question, but one I had in the past when coming away from VBA scipts to php, where you could us "GoTo" to re-enter a loop "On Error" with a "Resume" and away it went still processing the function.
In php, after a bit of trial and error, I now use nested try{} catch{} for critical versus non critical processes, or even for interdependent class calls so I can trace my way back to the start of the error. e.g. if function b is dependant on function a, but function c is a nice to have but should not stop the process, and I still want to know the outcomes of all 3 regardless, here's what I do:

//set up array to capture output of all 3 functions
$resultArr = array(array(), array(), array());

// Loop through the primary array and run the functions 
foreach($x as $key => $val)
{
    try
    {
        $resultArr[$key][0][] = a($key); 
        $resultArr[$key][1][] = b($val);
        try
        { // If successful, output of c() is captured
            $resultArr[$key][2][] = c($key, $val);
        }
        catch(Exception $ex)
        { // If an error, capture why c() failed
            $resultArr[$key][2][] = $ex->getMessage();
        }
    }
    catch(Exception $ex)
    { // If critical functions a() or b() fail, we catch the reason why
        $criticalError = $ex->getMessage();
    }
} 

Now I can loop through my result array for each key and assess the outcomes. If there is a critical failure for a() or b().
I still have a point of reference on how far it got before a critical failure occurred within the $resultArr and if the exception handler is set correctly, I know if it was a() or b() that failed.
If c() fails, loop keeps going. If c() failed at various points, with a bit of extra post loop logic I can even find out if c() worked or had an error on each iteration by interrogating $resultArr[$key][2].

Roy
  • 17
  • 6
0

You can, but I will warn: many consider this method quite evil.

// https://stackoverflow.com/a/66377817/578023
function is_same(&$a, &$b): bool {
    $_ = [ &$a, &$b ];
    return
        \ReflectionReference::fromArrayElement($_, 0)->getId() ===
        \ReflectionReference::fromArrayElement($_, 1)->getId();
}

function attempt_risky_action($collection){
    $cursor=NULL;
    $resuming = false;
    
    resume:
    try{
        foreach($collection as $item){
            if($resuming && !is_same($cursor,$item) ){
                continue;                   // some things have better ways to skip ahead, especially an array index
            }
            else {
                $resuming = false;
                $cursor=&$item;             // main concept is to remember where you are in the iteration
            }                               // in some situation you may have to use references,  &item
            

            // your normal loop here
            .
            .
            .
        }
    }   catch( Exception $e){
        $resuming = repair_something($e, $collection);      // returns false if your repair ran out of ideas
        if($resuming)
            goto resume;
    }
    
    unset($cursor);
    
}

Ideally it would be best to wrap the unset($cursor); call in a finally{} block, but frankly I'm not sure how that plays with goto off hand.

If it executes because goto broke the flow then you will need some conditional logic, so the cursor still exists. If you have a return statement inside the loop you must use a finally block for call to unset($cursor) -- or cause a memory leak.

Then again, while less exciting, you can do this same trick by just nesting your whole loop in do{ try/catch } while($resuming). While that is not LITERALLY reversing your execution, it does exactly the same effect without risking a goto.

is_same() from https://stackoverflow.com/a/66377817/578023

function attempt_risky_action($collection){
    $cursor=NULL;
    $resuming = false;
    
    do{
        try{
            foreach($collection as $item){
                if($resuming && !is_same($cursor,$item) ){
                    continue;   
                }
                else {
                    $resuming = false;
                    $cursor=&$item;
                }

                // your loop here
            }
        }   catch( Exception $e){
            $resuming = repair_something($e, $collection);      // returns false if your repair ran out of ideas
        }
        finally{ 
            if(!$resuming){
                unset($cursor); 
            }
        }
    } while($resuming);
}

A last method, not pictured; you can use PHP's reset(), prev(), current(), next(), end() faculties

These will allow you instead to simply have your try/catch block inside a code block that iterates as a loop would -- then use prev() in the catch to try again.