php - try, catch, and retry
Asked Answered
F

9

52

Sometimes my code breaks and it is out of my control

How would I do the following?

try {
//do my stuff
}
catch {
//sleep and try again
}

The code isn't that much, so it's all one function, so I didn't want to make and call another function if I didn't have to

Franci answered 28/7, 2014 at 18:47 Comment(3)
How many times do you want to retry the same operation? Why would you want to retry an operation that fails? What exactly is out of your control?Maxa
A couple times. I use a third party api that comes down for a couple minutes every once in a while.Franci
@oliakaoil, if that "Why would you want to retry an operation that fails?" was asked in the general sense: in case of a recoverable error the ex. handler could fix things up and let the normal flow proceed by retrying. In fact, there's even an RFC for retry now: wiki.php.net/rfc/retry-keywordImbrue
E
92

You can try something like this:

function exception_error_handler($errno, $errstr, $errfile, $errline ) {
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler("exception_error_handler");

$NUM_OF_ATTEMPTS = 5;
$attempts = 0;

do {

    try
    {
        executeCode();
    } catch (Exception $e) {
        $attempts++;
        sleep(1);
        continue;
    }
    
    break;

} while($attempts < $NUM_OF_ATTEMPTS);

function executeCode(){
    echo "Hello world!";
}

Here, we perform a do...while loop so that the code is executed at least once. If the executeCode() function experiences an error, it will throw an Exception which the try...catch block will capture. The catch block will then increment the variable $attempt by one and call continue to test the while condition for the next iteration. If there have already been five attempts, the loop will exit and the script can continue. If there is no error, i.e. the continue statement from the catch block is not executed, the loop will break, thus finishing the script.

Note the use of the set_error_handler function taken from here. We do this so that all errors within the executeCode() function are caught, even if we don't manually throw the errors ourselves.

If you believe your code may fail numerous times, the sleep() function may be beneficial before the continue statement. 'Slowing' down the possibly infinite loop will help with lower your CPU Usage.

It is not a good idea to have a script run infinitely until it is successful, since an error that is present in the first 100 iterations of a loop, is unlikely to ever be resolved, thus causing the script to 'freeze' up. More oft than not, it is better to re-evaluate the code that you would like run multiple times in the case of an error, and improve it to properly handle any errors that come its way.

Excoriation answered 28/7, 2014 at 19:26 Comment(2)
This is only temporarily, so i will use this until i figure out a solution. I've edited it a bit to my use. thanksFranci
Great example (the best in this thread), but I don't think the set_error_handler() call is absolutely necessary unless you specifically want to catch every PHP notice, warning and syntax error in addition to the usual exceptions. A regular try/catch will still catch exceptions thrown from within PHP internal functions, as well as the ones you throw yourself.Sech
R
12

Simply :

function doSomething($params, $try = 1){
    try{
        //do something
        return true;
    }
    catch(Exception $e){
        if($try <5){
             sleep(10);
             //optionnaly log or send notice mail with $e and $try
             doSomething($params, $try++);
        }
        else{ 
             return false;
        }
    }
}
Rate answered 23/5, 2018 at 9:41 Comment(0)
U
9

Here is an easy algorithm:

$trys = 5;
do {
    try {
        // YOUR CODE
        // YOUR CODE
        // YOUR CODE
        // YOUR CODE
        // YOUR CODE

        break;
    }
    catch(Exception $e) {
        $trys--;
    }
} while($trys > 0);

Also, For infinite trys, Remove the $trys-- line :)

Unsuccess answered 15/8, 2018 at 10:41 Comment(0)
R
2

This library seems cool, it has different backoff strategies you can choose from and keeps you from implementing the retry logic every time.

https://github.com/stechstudio/backoff

A sample code:

$result = backoff(function() {
    return doSomeWorkThatMightFail();
});
Rapallo answered 13/2, 2020 at 10:55 Comment(0)
J
1

I don't entirely understand why you would want to, as there is a good chance you will create an infinite loop. However, if the code is likely to succeed after a small sleep, for whatever reason, below is a solution

while(true){
    // execute the code you are attempting
    if(some condition to signal success){
        break; // exit the loop
    }

    sleep(2); // 2 seconds
}
Jemina answered 28/7, 2014 at 19:19 Comment(2)
Retrying after sleep (but normally with a maximum number of attempts to avoid infinite loops) is very common when dealing with external services, that may be downLindemann
This is just retry on condition. I think the main request is how to retry with the try catch ? Only checking a certain condition is certainly far from what try catch that prevents all exception.Get
L
0

I recommend this library: PHP Backoff

It allows them to execute a code, if for some reason something happens and fails, it is automatically rerun as a task. It has several configurations so you can adapt it to your needs.

Instalalation:

composer require stechstudio/backoff

Quickstart

$result = backoff(function() {
    // Here is the function they want to execute.
    return doSomeWorkThatMightFail();
});

All documentation is here: https://github.com/stechstudio/backoff

Lauzon answered 6/12, 2021 at 0:30 Comment(0)
E
0

There is a very elegant solution embedded in Laravel for this type of problem, that can easily be ported to any php framework/project.

It uses a control-structure goto to go circle back to before the exception happend to run the code again, unless conditions are met.

https://www.php.net/manual/en/control-structures.goto.php

More information: https://laravel.com/docs/10.x/helpers#method-retry

function retry($times, callable $callback, $sleepMilliseconds = 0, $when = null)
    {
        $attempts = 0;

        $backoff = [];

        if (is_array($times)) {
            $backoff = $times;

            $times = count($times) + 1;
        }

        beginning:
        $attempts++;
        $times--;

        try {
            return $callback($attempts);
        } catch (Exception $e) {
            if ($times < 1 || ($when && ! $when($e))) {
                throw $e;
            }

            $sleepMilliseconds = $backoff[$attempts - 1] ?? $sleepMilliseconds;

            if ($sleepMilliseconds) {
                usleep(value($sleepMilliseconds, $attempts, $e) * 1000);
            }

            goto beginning;
        }
    }
Enfold answered 2/8, 2023 at 16:1 Comment(0)
M
0

Some good answers already but I like to avoid the use of "break;" for the expected (no problems) code path, just because it "feels" wrong or less readable.

Here's my version:

$tries = 5;
$success = false;

do {
    try {
        // [ your code here ]

        $success = true;
    }
    catch(Exception $e) {
        // [ any required cleanup ]
    }
    
} while (!$success && $tries--);

Then all you have to do is check $success afterwards.

This all assumes, of course, that your [ your code here ] block throws an exception on failure.

Manners answered 10/4, 2024 at 3:15 Comment(0)
A
-2

To be able to catch an exception you have to first throw one. Something like this.

<?php
function inverse($x) {
    if (!$x) {
        throw new Exception('Division by zero.');
    }
    return 1/$x;
}

try {
    echo inverse(5) . "\n";
    echo inverse(0) . "\n";
} catch (Exception $e) {
    echo 'Caught exception: ',  $e->getMessage(), "\n";
}

// Continue execution
echo "Hello World\n";
?>

There are certain built in exceptions : http://php.net/manual/en/spl.exceptions.php

Appearance answered 28/7, 2014 at 18:53 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.