divineomega/attempt

This package is abandoned and no longer maintained. The author suggests using the jord-jd/attempt package instead.

Attempt to run a function, retrying if needed

Maintainers

Package info

github.com/Jord-JD/attempt

pkg:composer/divineomega/attempt

Transparency log

Fund package maintenance!

DivineOmega

Statistics

Installs: 11

Dependents: 0

Suggesters: 0

Stars: 2

Open Issues: 0

v3.1.0 2026-07-18 05:30 UTC

This package is auto-updated.

Last update: 2026-07-18 05:31:20 UTC


README

Retry a PHP callable when an operation fails.

Installation

composer require jord-jd/attempt

The package supports PHP 7.1 through current PHP 8.x releases.

Usage

$result = attempt(function () {
    return fetchFromAnUnreliableApi();
})
    ->maxAttempts(5)
    ->withGap(2)
    ->now();

maxAttempts(5) means at most five total executions, including the first one. Use maxAttempts(0), the default, for no attempt limit. The last exception is available as the previous exception on MaxAttemptsExceeded.

Exponential or custom backoff

$result = attempt($operation)
    ->maxAttempts(5)
    ->withExponentialBackoff(1, 2.0, 30)
    ->now();

Successive delays are 1, 2, 4, and 8 seconds, with later retries capped at 30 seconds. For complete control, return a non-negative integer number of seconds from withBackoff():

use Exception;

$result = attempt($operation)
    ->withBackoff(function (int $failedAttempt, Exception $exception): int {
        return min(60, $failedAttempt * 5);
    })
    ->now();

Calling withGap() after withBackoff() switches back to a fixed delay.

Retry selected exceptions

Do not retry permanent failures:

$result = attempt($operation)
    ->retryIf(function (Exception $exception, int $failedAttempt): bool {
        return $exception instanceof TemporaryApiException;
    })
    ->maxAttempts(5)
    ->now();

When the predicate returns false, the original exception is rethrown.

Observe retries

attempt($operation)
    ->onRetry(function (Exception $exception, int $failedAttempt, int $delay): void {
        logger()->warning('Operation failed; retrying', [
            'attempt' => $failedAttempt,
            'delay' => $delay,
            'exception' => $exception,
        ]);
    })
    ->now();

The callback runs only when another execution will actually be attempted.

Time limits and scheduled starts

// Retry until this instant. No execution is started at or after the deadline.
attempt($operation)
    ->until(new DateTimeImmutable('+30 seconds'))
    ->withGap(5)
    ->now();

// Wait until this instant, then begin. A past time starts immediately.
attempt($operation)
    ->at(new DateTimeImmutable('+5 seconds'));

If the next configured delay would cross the deadline, Attempt waits only until the deadline and throws DateTimeExceeded without starting a late execution.

All configuration methods can be chained. Negative attempt limits or gaps and invalid exponential-backoff values throw InvalidArgumentException immediately.