Retry a callable with exponential backoff in PHP
Re-run flaky operations with growing delays and jitter, only for chosen exception types.
- PHP
- GreenSnippets
- MIT
- Executed with PHP 8.2
<?php
/**
* @template T
* @param callable(): T $operation
* @param array<class-string<Throwable>> $retryOn
* @return T
*/
function retry(callable $operation, int $attempts = 3, int $baseDelayMs = 200, array $retryOn = [Exception::class], ?callable $sleep = null)
{
$sleep ??= fn (int $ms) => usleep($ms * 1000);
for ($attempt = 1; ; $attempt++) {
try {
return $operation();
} catch (Throwable $e) {
$retryable = array_filter($retryOn, fn ($class) => $e instanceof $class);
if (!$retryable || $attempt >= $attempts) {
throw $e;
}
$delay = $baseDelayMs * (2 ** ($attempt - 1));
$sleep($delay + random_int(0, intdiv($delay, 2)));
}
}
}Example / expected output
$body = retry(fn () => fetchRemote($url), attempts: 4, retryOn: [RuntimeException::class]);How it works
Use for idempotent operations such as HTTP GETs. Delays double each attempt, with random jitter so many workers do not retry in lockstep. Exceptions that are not in $retryOn propagate immediately, and the last exception is rethrown when attempts run out. The sleep function is injectable for testing.
Related snippets
- TypeScript Tested
Typed fetch helper that checks the response
A fetch wrapper that throws on HTTP errors and validates the JSON body with a type guard.
- JavaScript Tested
Verify a webhook signature in Node.js
Validate an HMAC-SHA256 webhook against the raw request body with a constant-time comparison and replay window.
- PHP Tested
Decode JSON safely in PHP
Parse JSON with exceptions on error and a depth limit, instead of silently getting null.
- JavaScript Tested
Retry an async operation with exponential backoff
Retry a failing promise-returning function with growing, jittered delays and a retry predicate.

