philiprehberger / php-retry
Composable retry utility with exponential backoff, jitter, and exception filtering
v1.1.1
2026-03-17 20:06 UTC
Requires
- php: ^8.2
Requires (Dev)
- laravel/pint: ^1.0
- phpstan/phpstan: ^1.12|^2.0
- phpunit/phpunit: ^11.0
README
Composable retry utility with exponential backoff, jitter, and exception filtering.
Requirements
| Requirement | Version |
|---|---|
| PHP | ^8.2 |
Installation
composer require philiprehberger/php-retry
Usage
Basic retry
use PhilipRehberger\Retry\Retry; $result = Retry::times(3)->run(function () { return file_get_contents('https://api.example.com/data'); }); echo $result->value; // The response body echo $result->attempts; // Number of attempts made echo $result->totalTimeMs; // Total time spent in milliseconds
Exponential backoff with jitter
$result = Retry::times(5) ->backoff(baseMs: 100, maxMs: 5000) ->jitter() ->run(fn () => $httpClient->get('/unstable-endpoint'));
Linear backoff
$result = Retry::times(3) ->linear(delayMs: 200) ->run(fn () => $api->call());
Constant delay
$result = Retry::times(4) ->constant(delayMs: 500) ->run(fn () => $service->fetch());
Exception filtering
Only retry on specific exceptions:
$result = Retry::times(5) ->onlyIf(fn (\Throwable $e) => $e instanceof ConnectionException) ->run(fn () => $db->query($sql));
Exclude specific exceptions from retrying:
$result = Retry::times(5) ->except(ValidationException::class, AuthenticationException::class) ->run(fn () => $api->submit($data));
Conditional Retry
Use shouldRetry() to provide a predicate that receives the exception and attempt number:
$retry = Retry::times(5) ->shouldRetry(function (\Throwable $e, int $attempt): bool { // Stop retrying after attempt 3 for rate-limit errors if ($e instanceof RateLimitException && $attempt >= 3) { return false; } return true; }) ->run(fn () => $api->request());
Use retryOnlyOn() to retry only for specific exception types:
$result = Retry::times(5) ->retryOnlyOn(ConnectionException::class, TimeoutException::class) ->run(fn () => $httpClient->get('/endpoint'));
Retrieve the total number of attempts after execution with getAttempts():
$retry = Retry::times(5); $result = $retry->run(fn () => $service->call()); echo $retry->getAttempts(); // e.g. 3
Time budget
Stop retrying after a total time budget is exceeded:
$result = Retry::times(100) ->constant(delayMs: 50) ->maxDuration(ms: 2000) ->run(fn () => $service->call());
Retry forever (with safety)
$result = Retry::forever() ->backoff(baseMs: 100, maxMs: 30000) ->jitter() ->maxDuration(ms: 60000) ->run(fn () => $queue->consume());
Callbacks
$result = Retry::times(5) ->backoff(baseMs: 100) ->beforeRetry(function (int $attempt, \Throwable $e) { logger()->warning("Retry attempt {$attempt}: {$e->getMessage()}"); }) ->afterRetry(function (int $attempt, ?\Throwable $e) { if ($e === null) { logger()->info("Succeeded on attempt {$attempt}"); } }) ->run(fn () => $api->request());
API
| Method | Description |
|---|---|
Retry::times(int $maxAttempts) |
Create a retry builder with a maximum number of attempts |
Retry::forever() |
Create a retry builder that retries indefinitely |
->backoff(bool $exponential, int $baseMs, int $maxMs) |
Configure exponential backoff |
->linear(int $delayMs) |
Configure linear backoff |
->constant(int $delayMs) |
Configure constant delay |
->jitter(bool $enabled) |
Enable or disable jitter |
->onlyIf(callable $predicate) |
Only retry when predicate returns true |
->except(string ...$exceptionClasses) |
Exclude specific exception types from retrying |
->shouldRetry(callable $predicate) |
Predicate receiving exception and attempt number; return false to stop |
->retryOnlyOn(string ...$exceptionClasses) |
Only retry for the given exception types (sugar for shouldRetry) |
->getAttempts() |
Get the total number of attempts made after execution |
->maxDuration(int $ms) |
Set maximum total duration for all attempts |
->beforeRetry(callable $callback) |
Callback invoked before each retry |
->afterRetry(callable $callback) |
Callback invoked after each attempt |
->run(callable $operation) |
Execute the operation with retry logic |
Development
composer install vendor/bin/phpunit vendor/bin/pint --test vendor/bin/phpstan analyse
License
MIT