Skip to the guide
All guides

API reference

Every public class, interface and enum of Reqtide, with its signatures and a short note on what each method does. Read it next to the guides.

app Reqtide PHP 0.1.0checked reading 34 minutes

On this page

This page lists the public surface of enaxon/reqtide, enaxon/reqtide-redis and enaxon/reqtide-guzzle. The signatures are read from the source code, so they match the release. Types and methods that are marked internal are not listed and may change at any time.

Start with Getting started if you have not used the library yet.

Build a pipeline

Everything you need at start-up. PipelineBuilder assembles and validates the configuration, compileInMemory() wires process-local state, and the result is a CompiledPipeline you reuse. Every number is yours: the builder has no defaults.

PipelineBuilder

Fluent, immutable builder: every call returns a new builder. profile(), partition() and attemptTimeout() are required; a policy exists only if you call its method. A bad value raises InvalidConfigurationException naming the option.

PHP
final class PipelineBuilder {
    public static function profile(string $id, string $version): self;
    public function partition(
        string $application,
        string $environment,
        string $subject,
        ?string $tenant = null,
    ): self;
    public function attemptTimeout(
        Duration $maximumDuration,
        AttemptTimeoutRequirement $requirement,
    ): self;
    public function retry(
        int $maxAttempts,
        BackoffMode $backoff,
        Duration $baseDelay,
        Duration $maximumDelay,
        JitterMode $jitter,
        ?Duration $minimumRemainingAttemptAllowance = null,
    ): self;
    public function retryBudget(
        int $capacityTokens,
        int $refillTokens,
        Duration $refillInterval,
        Duration $reservationLease,
    ): self;
    public function rateLimit(
        RateAlgorithmOptions $algorithm,
        int $cost = 1,
    ): self;
    public function circuitBreaker(
        CircuitWindowOptions $window,
        int $minimumSamples,
        Ratio $failureThreshold,
        Duration $openDuration,
        int $probeCapacity,
        int $probeSuccessThreshold,
        Duration $permitLease,
    ): self;
    public function bulkhead(int $capacity, Duration $lease): self;
    public function cooldown(
        Duration $cleanupGrace,
        ?Duration $maximumExtension = null,
    ): self;
    public function fallbackScope(FallbackScope $scope): self;
    public static function tokenBucket(
        int $capacity,
        int $refillTokens,
        Duration $refillInterval,
    ): TokenBucketOptions;
    public static function fixedWindow(
        int $capacity,
        Duration $window,
        FixedWindowAnchor $anchor,
    ): FixedWindowOptions;
    public static function slidingWindow(
        int $capacity,
        Duration $window,
    ): SlidingCounterOptions;
    public static function lastCalls(int $completedSamples): CountCircuitWindow;
    public static function timeWindow(
        Duration $window,
        Duration $bucketGranularity,
    ): TimeCircuitWindow;
    public function definition(): PipelineDefinition;
    public function compile(RuntimeEnvironment $environment): CompiledPipeline;
    public function compileInMemory(
        OutcomeClassifier $classifier,
        ?EventSink $events = null,
    ): CompiledPipeline;
}
  • profile() Names and versions the configuration. Change the version when settings change; shared state is keyed by it.
  • partition() Whose state this is: application, environment, subject and an optional tenant. One pipeline serves exactly one partition.
  • attemptTimeout() The longest one attempt may take. Use Cooperative with the synchronous runtime.
  • retry() Up to maxAttempts attempts, the first included. More than one needs retryBudget() and a replay-safety assertion on every call.
  • retryBudget() A token pool that pays for retries so a failing dependency is not hammered.
  • rateLimit() Charges cost units per dispatch, retries included. Takes the result of tokenBucket(), fixedWindow() or slidingWindow().
  • circuitBreaker() Judges a window of completed calls, opens at a failure share, probes after the open duration.
  • bulkhead() At most capacity operations in flight; lease bounds a slot whose owner vanished.
  • cooldown() A shared pause after a server-stated wait. Needs a classifier that reports waits.
  • fallbackScope() Whether a fallback may also replace admission refusals.
  • tokenBucket() Rate algorithm: capacity, refill tokens per interval.
  • fixedWindow() Rate algorithm: capacity per window, aligned to the epoch or to the first hit.
  • slidingWindow() Rate algorithm: an approximate sliding counter.
  • lastCalls() Circuit window over the last N completed calls.
  • timeWindow() Circuit window over a duration, kept in buckets.
  • definition() Validates the composition and returns the immutable PipelineDefinition.
  • compile() Compiles against a hand-built RuntimeEnvironment, for example with test helpers.
  • compileInMemory() Compiles with the real clock, sleeping delays and process-local memory backends. There is no default classifier.

CompiledPipeline

The reusable pipeline. It holds the state that makes limits work, so build it once and share it. It has no per-operation state.

PHP
final class CompiledPipeline {
    public function newContext(
        Duration $timeout,
        ?OperationSafety $safety = null,
        ?FallbackOperation $fallback = null,
        ?Cancellation $cancellation = null,
        ?BoundedMetadata $attributes = null,
        OperationId|string|null $operationId = null,
        ?PartitionIdentity $partition = null,
    ): ExecutionContext;
    public function execute(
        callable $operation,
        ExecutionContext $context,
    ): mixed;
    public function executeDetailed(
        callable $operation,
        ExecutionContext $context,
    ): ExecutionCompletion;
    public function executeOperation(
        AttemptOperation $operation,
        ExecutionContext $context,
    ): mixed;
    public function executeOperationDetailed(
        AttemptOperation $operation,
        ExecutionContext $context,
    ): ExecutionCompletion;
    public function explain(): PipelineExplanation;
}
  • newContext() One context per operation: the total deadline, the replay-safety statement, an optional fallback, cancellation and an operation id.
  • execute() Runs a callable through the pipeline. Returns your value, throws your own exception unchanged, or throws a Reqtide refusal.
  • executeDetailed() Same run, but returns an ExecutionCompletion instead of throwing for business or policy outcomes.
  • executeOperation() Like execute() for an AttemptOperation object instead of a callable.
  • executeOperationDetailed() Like executeDetailed() for an AttemptOperation.
  • explain() Describes the configured policies and the runtime capabilities. No I/O and no state change.

InMemoryEnvironment

Wires the runtime for one process: system clock, a scheduler that really sleeps, secure randomness and a memory backend for each configured policy. This is what compileInMemory() uses.

PHP
final class InMemoryEnvironment {
    public static function for(
        PipelineDefinition $definition,
        OutcomeClassifier $classifier,
        ?EventSink $events = null,
    ): RuntimeEnvironment;
}

PipelineExplanation

What explain() returns: profile, enabled policies and capability notes such as physical_stop=not_guaranteed.

PHP
final readonly class PipelineExplanation {
    public function __construct(
        public ProfileId $profileId,
        public ProfileVersion $profileVersion,
        public array $enabledPolicies,
        public array $capabilityNotes,
    );
}

Duration

A non-negative integer count of nanoseconds. Arithmetic is checked: overflow throws and never wraps.

PHP
final readonly class Duration {
    public function __construct(public int $nanoseconds);
    public static function zero(): self;
    public static function nanoseconds(int $value): self;
    public static function microseconds(int $value): self;
    public static function milliseconds(int $value): self;
    public static function seconds(int $value): self;
    public function isZero(): bool;
    public function isPositive(): bool;
    public function add(self $other): self;
    public function subtract(self $other): self;
    public function saturatingSubtract(self $other): self;
    public function multiply(int $factor): self;
    public function min(self $other): self;
    public function max(self $other): self;
    public function compare(self $other): int;
    public function equals(self $other): bool;
    public function ceilMicroseconds(): int;
}

Ratio

A reduced fraction in (0, 1], compared with integers. Used for the circuit failure threshold.

PHP
final readonly class Ratio {
    public const MAX_COMPONENT = 9007199254740991;
    public function __construct(
        public PositiveInt $numerator,
        public PositiveInt $denominator,
    );
    public static function of(int $numerator, int $denominator): self;
}

Run an operation

Per-operation objects. A context carries the total deadline and your statement about repeating the call; your callable receives an AttemptContext.

ExecutionContext

Immutable facts of one logical operation. Build it with CompiledPipeline::newContext(). Do not share one between operations.

PHP
final readonly class ExecutionContext {
    public function __construct(
        public OperationId $operationId,
        public PartitionIdentity $partition,
        public Deadline $deadline,
        public Cancellation $cancellation,
        public OperationMetadata $metadata,
        public ?FallbackOperation $fallback,
    );
}

AttemptContext

Passed to your callable on every attempt: operation id, attempt id, the attempt number, the deadline, the cancellation and the attempt timeout.

PHP
final readonly class AttemptContext {
    public function __construct(
        public OperationId $operationId,
        public AttemptId $attemptId,
        public int $dispatchedAttemptNumber,
        public Deadline $deadline,
        public Cancellation $cancellation,
        public Duration $attemptTimeout,
    );
}

OperationSafety

Your statement about repeating a call. unsafe() is the default and forbids retries; the other two allow them.

PHP
final readonly class OperationSafety {
    public static function unsafe(): self;
    public static function replayableRead(ReplaySafetyContract $contract): self;
    public static function providerIdempotentMutation(
        ReplaySafetyContract $contract,
    ): self;
}
  • unsafe() The default. A pipeline that allows more than one attempt refuses to run.
  • replayableRead() You assert that running the callable again is safe.
  • providerIdempotentMutation() You assert that the provider deduplicates repeated mutations.

ReplaySafetyContract

The written assertion behind OperationSafety. Reqtide never verifies it.

PHP
final readonly class ReplaySafetyContract {
    public static function forReadOnlyCallable(
        string $operationName,
        Duration $validFor,
        ?Clock $clock = null,
        ?array $retryableOutcomeCodes = null,
    ): self;
    public function __construct(
        public string $contractReference,
        public string $identityValue,
        public string $identityScope,
        public string $duplicateHandlingContract,
        public DateTimeImmutable $establishedAt,
        public DateTimeImmutable $validUntil,
        public Duration $retention,
        public ReplayPreparation $preparation,
        public array $retryableOutcomeCodes,
    );
}
  • forReadOnlyCallable() Builds the contract for a repeatable read. Your call is the assertion; it is not checked.

OperationSafetyKind

The kind of safety an operation carries.

PHP
enum OperationSafetyKind: string {
    case Unsafe = 'unsafe';
    case ReplayableRead = 'replayable_read';
    case ProviderIdempotentMutation = 'provider_idempotent_mutation';
}

FallbackOperation

A fallback you attach to one operation through newContext(). It receives the original outcome.

PHP
interface FallbackOperation {
    public function invoke(Outcome $original, ExecutionContext $context): mixed;
}

FallbackScope

What a fallback may replace. The default replaces business outcomes only.

PHP
enum FallbackScope: string {
    case BusinessOutcomesOnly = 'business_outcomes_only';
    case IncludeAdmissionRefusals = 'include_admission_refusals';
}

Decide what an outcome means

Policies act on the classification of each attempt, and you decide what a failure is. ExceptionClassifier is the ready-made classifier; implement OutcomeClassifier for anything it cannot express.

ExceptionClassifier

Classifier driven by lists of exception classes. Conservative on purpose: an exception nobody listed is not retried and not replaced by a fallback, but it counts as a circuit failure. Matching is instanceof. Every method returns a new instance.

PHP
final readonly class ExceptionClassifier implements OutcomeClassifier {
    public static function create(): self;
    public function retryOn(string ...$exceptionClasses): self;
    public function countAsCircuitFailure(string ...$exceptionClasses): self;
    public function ignoreForCircuit(string ...$exceptionClasses): self;
    public function fallbackOn(string ...$exceptionClasses): self;
    public function retryAfterFrom(callable $extractor): self;
    public function unacceptableValuesWhen(callable $predicate): self;
    public function classify(
        Outcome $outcome,
        OperationMetadata $metadata,
    ): OutcomeClassification;
}
  • create() An empty classifier.
  • retryOn() Exception classes that may be retried.
  • countAsCircuitFailure() Narrows circuit failures to these classes (and retryable ones). Without it every exception that is not ignored counts.
  • ignoreForCircuit() Exception classes that say nothing about the dependency, such as validation errors. Ignore wins over a failure list.
  • fallbackOn() Exception classes a fallback may replace.
  • retryAfterFrom() Extracts a server-stated wait (a Duration or null) from an exception.
  • unacceptableValuesWhen() Marks a returned value as a failure: a circuit failure, never retried, never replaced.
  • classify() The OutcomeClassifier contract.

OutcomeClassifier

The contract a classifier implements. Write your own when results must be retried or classified in ways the exception lists cannot say.

PHP
interface OutcomeClassifier {
    public function classify(
        Outcome $outcome,
        OperationMetadata $metadata,
    ): OutcomeClassification;
}

Outcome

What one attempt produced: a returned value or a thrown exception.

PHP
final readonly class Outcome {
    public static function fromValue(
        mixed $value,
        BoundedMetadata $metadata,
    ): self;
    public static function fromException(
        Throwable $exception,
        BoundedMetadata $metadata,
    ): self;
    public function value(): mixed;
    public function exception(): Throwable;
}

OutcomeClassification

The classifier's verdict on one outcome: retry eligibility, circuit verdict, fallback eligibility, acceptability, a reason code and an optional wait lower bound.

PHP
final readonly class OutcomeClassification {
    public function __construct(
        public RetryEligibility $retry,
        public CircuitVerdict $circuit,
        public FallbackEligibility $fallback,
        public Acceptability $acceptability,
        public string $reasonCode,
        public ?Duration $retryAfterLowerBound,
    );
}

OutcomeKind

Whether an outcome is a value or an exception.

PHP
enum OutcomeKind: string {
    case Value = 'value';
    case Exception = 'exception';
}

RetryEligibility

Whether an outcome may be retried.

PHP
enum RetryEligibility: string {
    case Eligible = 'eligible';
    case Ineligible = 'ineligible';
}

FallbackEligibility

Whether a fallback may replace an outcome.

PHP
enum FallbackEligibility: string {
    case Eligible = 'eligible';
    case Ineligible = 'ineligible';
}

CircuitVerdict

How an outcome counts for the circuit breaker. Neutral outcomes neither open nor heal it.

PHP
enum CircuitVerdict: string {
    case Success = 'success';
    case Failure = 'failure';
    case Neutral = 'neutral';
}

Acceptability

Whether an outcome is acceptable to the caller.

PHP
enum Acceptability: string {
    case Acceptable = 'acceptable';
    case Unacceptable = 'unacceptable';
}

Policy options

Enums and option objects the builder takes or returns. You normally reach them through PipelineBuilder and rarely construct them yourself.

BackoffMode

Retry delay growth: constant or exponential.

PHP
enum BackoffMode: string {
    case Constant = 'constant';
    case Exponential = 'exponential';
}

JitterMode

Full draws each delay uniformly from zero to the computed delay.

PHP
enum JitterMode: string {
    case None = 'none';
    case Full = 'full';
}

AttemptTimeoutRequirement

Cooperative is the honest choice for the synchronous runtime: your code must respect the time. TransportEnforced needs a runtime that declares it.

PHP
enum AttemptTimeoutRequirement: string {
    case Cooperative = 'cooperative';
    case TransportEnforced = 'transport_enforced';
}

FixedWindowAnchor

Where fixed windows start: on multiples of the window length, or at the first hit.

PHP
enum FixedWindowAnchor: string {
    case Aligned = 'aligned';
    case FirstHit = 'first_hit';
}

TokenBucketOptions

Token-bucket settings returned by PipelineBuilder::tokenBucket().

PHP
final readonly class TokenBucketOptions implements RateAlgorithmOptions {
    public function __construct(
        public PositiveInt $capacityUnits,
        public PositiveInt $refillUnits,
        public Duration $refillInterval,
    );
}

FixedWindowOptions

Fixed-window settings returned by PipelineBuilder::fixedWindow().

PHP
final readonly class FixedWindowOptions implements RateAlgorithmOptions {
    public function __construct(
        public PositiveInt $capacityUnits,
        public Duration $window,
        public FixedWindowAnchor $anchor,
    );
}

SlidingCounterOptions

Sliding-counter settings returned by PipelineBuilder::slidingWindow().

PHP
final readonly class SlidingCounterOptions implements RateAlgorithmOptions {
    public function __construct(
        public PositiveInt $capacityUnits,
        public Duration $window,
    );
}

CountCircuitWindow

Circuit window over the last N completed calls.

PHP
final readonly class CountCircuitWindow implements CircuitWindowOptions {
    public function __construct(public PositiveInt $completedSamples);
}

TimeCircuitWindow

Circuit window over a duration, kept in buckets.

PHP
final readonly class TimeCircuitWindow implements CircuitWindowOptions {
    public function __construct(
        public Duration $window,
        public Duration $bucketGranularity,
    );
}

Results of an operation

execute() returns your value or throws. executeDetailed() returns the same facts as data in an ExecutionCompletion.

ExecutionCompletion

Disposition, terminal outcome, per-attempt records, bounded diagnostics and a summary of counts.

PHP
final readonly class ExecutionCompletion {
    public function __construct(
        public CompletionDisposition $disposition,
        public ?Outcome $terminalOutcome,
        public ?Outcome $fallbackOutcome,
        public ?ReqtideException $policyCause,
        public ?ClassificationFailedException $classifierCause,
        public array $attempts,
        public array $diagnostics,
        public ExecutionSummary $summary,
    );
    public function problem(): ?Problem;
}
  • problem() The redacted Problem for this completion, or null when there is nothing for Reqtide to report.

CompletionDisposition

How an operation ended.

PHP
enum CompletionDisposition: string {
    case Succeeded = 'succeeded';
    case Failed = 'failed';
    case Rejected = 'rejected';
    case Deferred = 'deferred';
    case Cancelled = 'cancelled';
    case DeadlineExceeded = 'deadline_exceeded';
    case FallbackSucceeded = 'fallback_succeeded';
    case FallbackFailed = 'fallback_failed';
}

ExecutionSummary

Counts for one operation: dispatches, retries, outcomes, fallbacks, elapsed time and whether accounting is complete.

PHP
final readonly class ExecutionSummary {
    public function __construct(
        public OperationId $operationId,
        public ProfileVersion $profileVersion,
        public CompletionDisposition $disposition,
        public int $dispatched,
        public int $admittedNotDispatched,
        public int $retries,
        public int $successfulAttempts,
        public int $failedAttempts,
        public int $neutralAttempts,
        public int $actualValues,
        public int $actualExceptions,
        public int $fallbackExecutions,
        public bool $accountingComplete,
        public int $omittedDiagnostics,
        public ?Duration $elapsed,
        public int $admissionUncertain = 0,
    );
}

AttemptRecord

One physical attempt: its outcome, classification and circuit verdict.

PHP
final readonly class AttemptRecord {
    public function __construct(
        public AttemptId $attemptId,
        public int $dispatchedAttemptNumber,
        public ActualAttemptDisposition $disposition,
        public Outcome $outcome,
        public ?Duration $elapsed,
        public SettlementOverride $logicalOverride,
        public ?OutcomeClassification $classification,
        public CircuitVerdict $circuitVerdict,
    );
}

Diagnostic

A bounded note about a stage and reason, such as retry / attempts_exhausted.

PHP
final readonly class Diagnostic {
    public function __construct(
        public string $stageCode,
        public string $reasonCode,
    );
    public static function of(Stage $stage, ReasonCode $reason): self;
    public function stage(): ?Stage;
    public function reason(): ?ReasonCode;
}

Errors and reasons

Your own exceptions come out unwrapped. A Reqtide decision is a typed exception that implements ProblemCarrier. Branch on reason() and problem(), never on message text.

Problem

Immutable, redaction-safe report shaped after RFC 9457 problem details. It is built from code metadata only: no message, URL, header or credential can enter it. Member names and order are API; title and detail wording is not.

PHP
final readonly class Problem {
    public const TYPE_PREFIX = 'tag:enaloo.com,2026:reqtide:problem:';
    public ?Stage $stage;
    public ?string $correlationId;
    public array $causes;
    public function __construct(
        public ReasonCode $code,
        ?Stage $stage = null,
        public ?OperationId $operationId = null,
        public ?ProfileVersion $profileVersion = null,
        public ?AttemptId $attemptId = null,
        ?string $correlationId = null,
        array $causes = [],
    );
    public function withContext(
        OperationId $operationId,
        ProfileVersion $profileVersion,
        ?AttemptId $attemptId = null,
        ?string $correlationId = null,
    ): self;
    public function type(): string;
    public function title(): string;
    public function category(): Category;
    public function severity(): Severity;
    public function retryability(): Retryability;
    public function detail(): string;
    public function toArray(): array;
    public function jsonSerialize(): array;
}

ProblemCarrier

Implemented by every exception that describes a Reqtide decision.

PHP
interface ProblemCarrier extends ReqtideException {
    public function reason(): ReasonCode;
    public function problem(): Problem;
}

ReqtideException

Marker for every exception raised by Reqtide itself. Your own exceptions are never wrapped in it.

PHP
interface ReqtideException {
}

ReasonCode

The registry of stable reason codes. Keep a default branch when you match over it: new cases may be added.

PHP
enum ReasonCode: string {
    case CircuitOpen = 'circuit_open';
    case CircuitProbeCapacityExhausted = 'circuit_probe_capacity_exhausted';
    case ConcurrencyCapacityExhausted = 'concurrency_capacity_exhausted';
    case OwnershipLeaseInsufficient = 'ownership_lease_insufficient';
    case CooldownActive = 'cooldown_active';
    case CooldownExceedsDeadline = 'cooldown_exceeds_deadline';
    case RateAccepted = 'rate.accepted';
    case RateCostExceedsCapacity = 'rate.cost_exceeds_capacity';
    case RateExhausted = 'rate.exhausted';
    case RateWaitUnrepresentable = 'rate.wait_unrepresentable';
    case AdmissionRejected = 'admission_rejected';
    case AdmissionDeferred = 'admission_deferred';
    case AdmissionImpossible = 'admission_impossible';
    case CircuitThresholdExceeded = 'circuit.threshold_exceeded';
    case CircuitProbeFailed = 'circuit.probe_failed';
    case CircuitProbesSucceeded = 'circuit.probes_succeeded';
    case CircuitOpenElapsed = 'circuit.open_elapsed';
    case RetryBudgetExhausted = 'retry_budget_exhausted';
    case RetryBudgetCommitRefused = 'retry_budget_commit_refused';
    case AttemptsExhausted = 'attempts_exhausted';
    case OutcomeNotRetryable = 'outcome_not_retryable';
    case RetryDelayExceedsDeadline = 'retry_delay_exceeds_deadline';
    case AttemptAllowanceUnavailable = 'attempt_allowance_unavailable';
    case ReplayContractNotValid = 'replay_contract_not_valid';
    case ReplayHorizonInadequate = 'replay_horizon_inadequate';
    case ReplayHorizonSaturated = 'replay_horizon_saturated';
    case RetryDeadlineReached = 'retry_deadline_reached';
    case RetryCancelled = 'retry_cancelled';
    case RetryAfterExceedsMaximumDelay = 'retry_after_exceeds_maximum_delay';
    case RetryAfterExceedsDeadline = 'retry_after_exceeds_deadline';
    case DelayFailed = 'delay_failed';
    case OperationCancelled = 'operation_cancelled';
    case DeadlineExceeded = 'deadline_exceeded';
    case StateUnavailable = 'unavailable';
    case StateCorrupt = 'corrupt';
    case StateIndeterminate = 'indeterminate';
    case StateCapacityExceeded = 'capacity_exceeded';
    case ClockFailure = 'clock_failure';
    case UnexpectedBackendFailure = 'unexpected';
    case RuntimeAdapterFailure = 'runtime_adapter_failure';
    case RateConsumeIndeterminate = 'rate_consume_indeterminate';
    case CommitNotConfirmed = 'commit_not_confirmed';
    case ClockUnavailable = 'clock_unavailable';
    case RetryClockUnavailable = 'retry_clock_unavailable';
    case FallbackClockUnavailable = 'fallback_clock_unavailable';
    case TimingUnavailable = 'timing_unavailable';
    case FallbackTimingUnavailable = 'fallback_timing_unavailable';
    case ElapsedUnavailable = 'elapsed_unavailable';
    case CancellationSampleFailed = 'cancellation_sample_failed';
    case RandomOutOfRange = 'random_out_of_range';
    case ConcurrencyReleaseFailed = 'concurrency_release_failed';
    case ConcurrencyReleaseStale = 'concurrency_release_stale';
    case CircuitAbandonFailed = 'circuit_abandon_failed';
    case CircuitAbandonStale = 'circuit_abandon_stale';
    case CircuitRecordFailed = 'circuit_record_failed';
    case CircuitRecordStale = 'circuit_record_stale';
    case RetryReservationReleaseFailed = 'retry_reservation_release_failed';
    case CooldownExtendFailed = 'cooldown_extend_failed';
    case CooldownExtensionCapped = 'cooldown_extension_capped';
    case SettlementAccounting = 'settlement_accounting';
    case CompletionSinkFailed = 'completion_sink_failed';
    case EventSinkFailed = 'event_sink_failed';
    case ClassifierFailed = 'classifier_failed';
    case LateSignalExtractionFailed = 'late_signal_extraction_failed';
    case FallbackFailed = 'fallback_failed';
    case DiscardFailed = 'discard_failed';
    case ValueAccepted = 'value_accepted';
    case ValueUnacceptable = 'value_unacceptable';
    case ExceptionRetryable = 'exception_retryable';
    case ExceptionFailure = 'exception_failure';
    case ExceptionIgnored = 'exception_ignored';
    case PreparationFailed = 'preparation_failed';
    case LifecycleFailure = 'lifecycle_failure';
    case UnexpectedFailure = 'unexpected_failure';
    case InvalidConfiguration = 'invalid_configuration';
    case CapabilityMismatch = 'capability_mismatch';
    public function category(): Category;
    public function severity(): Severity;
    public function retryability(): Retryability;
    public function stage(): ?Stage;
    public function title(): string;
}

Retryability

What a caller should do with a decision: wait, retry, fix the setup or reconcile.

PHP
enum Retryability: string {
    case Never = 'never';
    case AfterWait = 'after_wait';
    case Transient = 'transient';
    case Unknown = 'unknown';
}

Category

The kind of decision a code belongs to.

PHP
enum Category: string {
    case Policy = 'policy';
    case Infrastructure = 'infrastructure';
    case Configuration = 'configuration';
    case Classification = 'classification';
    case Fallback = 'fallback';
    case Admission = 'admission';
    case Retry = 'retry';
    case Accounting = 'accounting';
    case Preparation = 'preparation';
}

Severity

Advisory severity, using PSR-3 level names as plain strings.

PHP
enum Severity: string {
    case Debug = 'debug';
    case Info = 'info';
    case Notice = 'notice';
    case Warning = 'warning';
    case Error = 'error';
    case Critical = 'critical';
    public function psr3Level(): string;
    public function rank(): int;
    public function isAtLeast(self $threshold): bool;
}

Stage

The lifecycle stage a diagnostic or problem is reported at. Append-only.

PHP
enum Stage: string {
    case Preflight = 'preflight';
    case Preparation = 'preparation';
    case RetryBudget = 'retry_budget';
    case Admission = 'admission';
    case Barrier = 'barrier';
    case Dispatch = 'dispatch';
    case Settlement = 'settlement';
    case Classification = 'classification';
    case Finalize = 'finalize';
    case Cleanup = 'cleanup';
    case Discard = 'discard';
    case Retry = 'retry';
    case Fallback = 'fallback';
    case Observation = 'observation';
    case Summary = 'summary';
}

PolicyTerminationException

Base of the exceptions by which a policy ended the operation.

PHP
abstract class PolicyTerminationException implements ProblemCarrier, ReqtideException {
    public function problem(): Problem;
}

AdmissionRejectedException

A circuit, bulkhead or rate limit refused the call. Your callable did not run.

PHP
final class AdmissionRejectedException extends PolicyTerminationException {
    public function __construct(
        public readonly AdmissionDecision $decision,
        ?Throwable $previous = null,
    );
    public function reason(): ReasonCode;
}

AdmissionDeferredException

A refusal that carries a positive wait, for example a cooldown or a Retry-After that cannot be honored.

PHP
final class AdmissionDeferredException extends PolicyTerminationException {
    public function __construct(
        public readonly AdmissionDecision $decision,
        ?Throwable $previous = null,
    );
    public function reason(): ReasonCode;
}

ImpossibleAdmissionException

The request can never be admitted, for example a rate cost above the capacity.

PHP
final class ImpossibleAdmissionException extends PolicyTerminationException {
    public function __construct(
        public readonly AdmissionDecision $decision,
        ?Throwable $previous = null,
    );
    public function reason(): ReasonCode;
}

DeadlineExceededException

The total deadline passed. A late value is replaced by this.

PHP
final class DeadlineExceededException extends PolicyTerminationException {
    public function reason(): ReasonCode;
}

OperationCancelledException

The operation was cancelled through its cancellation.

PHP
final class OperationCancelledException extends PolicyTerminationException {
    public function reason(): ReasonCode;
}

FallbackFailedException

The fallback itself threw. getPrevious() is the fallback exception.

PHP
final class FallbackFailedException implements ProblemCarrier, ReqtideException {
    public function __construct(
        public readonly Outcome $originalOutcome,
        public readonly Throwable $fallbackCause,
    );
    public function reason(): ReasonCode;
    public function problem(): Problem;
}

InvalidConfigurationException

A value, option or combination is invalid. Raised before anything is dispatched.

PHP
final class InvalidConfigurationException implements ProblemCarrier, ReqtideException {
    public function reason(): ReasonCode;
    public function problem(): Problem;
}

CapabilityMismatchException

A configured behavior needs a capability the selected runtime, scheduler or backend does not declare.

PHP
final class CapabilityMismatchException implements ProblemCarrier, ReqtideException {
    public function reason(): ReasonCode;
    public function problem(): Problem;
}

InfrastructureException

Base of clock and state-backend failures. They are never hidden as success.

PHP
abstract class InfrastructureException implements ProblemCarrier, ReqtideException {
    public function problem(): Problem;
}

StateUnavailableException

A state backend could not be reached. The protected call does not run.

PHP
final class StateUnavailableException extends InfrastructureException {
    public function reason(): ReasonCode;
}

StateCapacityExceededException

A memory backend reached its ceiling and rejected a new partition.

PHP
final class StateCapacityExceededException extends InfrastructureException {
    public function reason(): ReasonCode;
}

CorruptStateException

A state backend found corrupt state.

PHP
final class CorruptStateException extends InfrastructureException {
    public function reason(): ReasonCode;
}

IndeterminateStateException

The result of a state-backend operation is indeterminate.

PHP
final class IndeterminateStateException extends InfrastructureException {
    public function reason(): ReasonCode;
}

ClockFailureException

A clock could not be read.

PHP
final class ClockFailureException extends InfrastructureException {
    public function reason(): ReasonCode;
}

ClassificationFailedException

The classifier itself threw.

PHP
final class ClassificationFailedException implements ProblemCarrier, ReqtideException {
    public function __construct(
        public readonly Outcome $actualOutcome,
        public readonly Throwable $classifierCause,
    );
    public function reason(): ReasonCode;
    public function problem(): Problem;
}

PreparationFailedException

Attempt preparation or replay verification failed. Nothing was dispatched.

PHP
final class PreparationFailedException implements ProblemCarrier, ReqtideException {
    public function __construct(public readonly Throwable $preparationCause);
    public function reason(): ReasonCode;
    public function problem(): Problem;
}

Events

Core emits its own typed events and has no telemetry-library dependency. Pass an EventSink to compileInMemory(). A failing sink never replaces your result.

EventSink

Receives every event. Implement it to log or count.

PHP
interface EventSink {
    public function emit(Event $event): void;
}

Event

Base contract of all events: each names its operation and profile version. EventSeverity::of() gives an event a severity.

PHP
interface Event {
    public function operationId(): OperationId;
    public function profileVersion(): ProfileVersion;
}

RetryScheduled

A retry was scheduled after a delay.

PHP
final readonly class RetryScheduled implements Event {
    public function __construct(
        private OperationId $operation,
        private ProfileVersion $profile,
        public int $nextDispatchedAttemptNumber,
        public Duration $delay,
        public string $reasonCode,
    );
    public function operationId(): OperationId;
    public function profileVersion(): ProfileVersion;
}

AdmissionRejected

A policy refused an attempt. policyKind and reasonCode say which.

PHP
final readonly class AdmissionRejected implements Event {
    public function __construct(
        private OperationId $operation,
        private ProfileVersion $profile,
        public string $policyKind,
        public string $reasonCode,
    );
    public function operationId(): OperationId;
    public function profileVersion(): ProfileVersion;
    public function reason(): ?ReasonCode;
    public function severity(): Severity;
}

AttemptCompleted

An attempt settled, with its circuit verdict.

PHP
final readonly class AttemptCompleted implements Event {
    public function __construct(
        private OperationId $operation,
        private ProfileVersion $profile,
        public AttemptId $attemptId,
        public CircuitVerdict $verdict,
        public ?Duration $elapsed,
    );
    public function operationId(): OperationId;
    public function profileVersion(): ProfileVersion;
}

OperationCompleted

The operation ended, with its disposition.

PHP
final readonly class OperationCompleted implements Event {
    public function __construct(
        private OperationId $operation,
        private ProfileVersion $profile,
        public CompletionDisposition $disposition,
        public ?Duration $elapsed,
        public bool $accountingComplete,
    );
    public function operationId(): OperationId;
    public function profileVersion(): ProfileVersion;
}

CircuitTransitioned

The circuit changed state.

PHP
final readonly class CircuitTransitioned implements Event {
    public function __construct(
        private OperationId $operation,
        private ProfileVersion $profile,
        public string $fromState,
        public string $toState,
        public string $reasonCode,
    );
    public function operationId(): OperationId;
    public function profileVersion(): ProfileVersion;
    public function reason(): ?ReasonCode;
    public function severity(): Severity;
}

BackendErrorObserved

A state backend reported an error.

PHP
final readonly class BackendErrorObserved implements Event {
    public function __construct(
        private OperationId $operation,
        private ProfileVersion $profile,
        public string $backendKind,
        public string $failureCode,
    );
    public function operationId(): OperationId;
    public function profileVersion(): ProfileVersion;
    public function reason(): ?ReasonCode;
    public function severity(): Severity;
}

AccountingDegraded

A bookkeeping step after the call failed or was stale. reasonCode says which.

PHP
final readonly class AccountingDegraded implements Event {
    public function __construct(
        private OperationId $operation,
        private ProfileVersion $profile,
        public string $reasonCode,
    );
    public function operationId(): OperationId;
    public function profileVersion(): ProfileVersion;
    public function reason(): ?ReasonCode;
    public function severity(): Severity;
}

NullEventSink

The default sink: discards every event.

PHP
final class NullEventSink implements EventSink {
    public function emit(Event $event): void;
}

EventSeverity

Severity of an event, with PSR-3 level names.

PHP
final class EventSeverity {
    public static function of(Event $event): Severity;
    public static function ofDisposition(
        CompletionDisposition $disposition,
    ): Severity;
}

Test helpers

In the Enaxon\Reqtide\Testing namespace, shipped in the same package. For tests only: never call them from production code. See Testing with the virtual clock.

VirtualClock

A clock that moves only when you move it. Starts at a fixed date so a test gives the same answer every day.

PHP
final class VirtualClock implements Clock {
    public const DEFAULT_WALL_START = '2026-01-01T00:00:00+00:00';
    public function __construct(
        int $initialMonotonicNanoseconds = 1000000000,
        ?DateTimeImmutable $initialWall = null,
    );
    public function startingAt(DateTimeImmutable $wall): self;
    public function monotonicNow(): int;
    public function wallNow(): DateTimeImmutable;
    public function advance(Duration $duration): void;
    public function advanceMonotonicOnly(Duration $duration): void;
    public function setMonotonic(int $nanoseconds): void;
    public function setWall(DateTimeImmutable $wall): void;
    public function failNextMonotonicReads(int $count = 1): void;
    public function failNextWallReads(int $count = 1): void;
    public function monotonicReadCount(): int;
    public function wallReadCount(): int;
    public function peekMonotonic(): int;
}

VirtualDelayScheduler

Advances a VirtualClock instead of sleeping, and records every requested delay.

PHP
final class VirtualDelayScheduler implements DelayScheduler {
    public function __construct(private readonly VirtualClock $clock);
    public function capability(): SchedulingCapability;
    public function cancelDuringNextDelay(
        CancellationToken $token,
        Duration $after,
    ): void;
    public function delay(
        Duration $duration,
        Deadline $deadline,
        Cancellation $cancellation,
    ): DelayDisposition;
    public function records(): array;
    public function requestedDelays(): array;
    public function callCount(): int;
    public function totalElapsed(): Duration;
}

ControlledRuntimeAdapter

A synchronous runtime driven by scripts: return a value, throw, advance the clock or request cancellation per dispatch.

PHP
final class ControlledRuntimeAdapter implements RuntimeAdapter {
    public function __construct(
        private readonly VirtualClock $clock,
        private readonly ?DispatchScript $default = null,
        private readonly ?RuntimeCapabilities $capabilities = null,
    );
    public static function defaultCapabilities(): RuntimeCapabilities;
    public function enqueue(DispatchScript ...$scripts): void;
    public function capabilities(): RuntimeCapabilities;
    public function invoke(
        PreparedAttempt $operation,
        AttemptContext $context,
        AttemptControls $controls,
    ): RuntimeExecution;
    public function dispatchCount(): int;
    public function dispatches(): array;
    public function attemptContexts(): array;
    public function pendingScripts(): int;
}

DispatchScript

One scripted dispatch.

PHP
final readonly class DispatchScript {
    public static function returning(mixed $value): self;
    public static function throwing(Throwable $exception): self;
    public static function callingOperation(): self;
    public function advancing(Duration $duration): self;
    public function requestingCancellation(CancellationToken $token): self;
}

RecordingEventSink

Stores every event for assertions.

PHP
final class RecordingEventSink implements EventSink {
    public function emit(Event $event): void;
    public function throwOnEmit(Throwable $failure, ?int $times = null): void;
    public function stopThrowing(): void;
    public function events(): array;
    public function ofType(string $class): array;
    public function count(): int;
    public function clear(): void;
}

SeededRandomSource

Seeded randomness: same seed, same sequence.

PHP
final class SeededRandomSource implements RandomSource {
    public function __construct(public readonly int $seed);
    public function integer(int $minimumInclusive, int $maximumInclusive): int;
    public function draws(): int;
}

ScriptedRandomSource

Returns a queued list of integers and fails loudly when it runs out.

PHP
final class ScriptedRandomSource implements RandomSource {
    public function __construct(array $values);
    public function enqueue(int ...$values): void;
    public function integer(int $minimumInclusive, int $maximumInclusive): int;
    public function remaining(): int;
    public function requests(): array;
}

Redis package: set up shared state

These classes belong to the separate package enaxon/reqtide-redis, namespace Enaxon\Reqtide\Redis. It is installed as a separate package. See Shared state with Redis. The host creates the registry and the ACL users. The application only builds the environment.

RedisEnvironment

The shared-state counterpart of InMemoryEnvironment. It builds a RuntimeEnvironment whose policies live in one Redis registry, for exactly the policies the definition configures. Creating it opens no connection.

PHP
final class RedisEnvironment {
    public static function for(
        PipelineDefinition $definition,
        OutcomeClassifier $classifier,
        RedisConnectionOptions $connection,
        RedisRegistry $registry,
        ?EventSink $events = null,
    ): RuntimeEnvironment;
    public static function explain(
        CompiledPipeline $pipeline,
        RedisConnectionOptions $connection,
        RedisRegistry $registry,
    ): PipelineExplanation;
}
  • for() Returns the environment to pass to PipelineBuilder::compile(). The clock of every decision is the Redis server clock.
  • explain() Adds the facts of the shared state to the usual explanation: state scope, I/O timeout, TLS, registry identity and ceilings. No I/O, and no host, password, prefix or secret in the output.

RedisRegistry

The immutable identity of one registry, the isolation unit of shared state: a key prefix, an identity, two ceilings and a secret of 32 bytes or more. The object creates nothing in Redis.

PHP
final readonly class RedisRegistry {
    public const SCHEMA_VERSION = 1;
    public const DEFAULT_MAX_PARTITIONS = 10000;
    public const DEFAULT_MAX_RECORDS = 20000;
    public function __construct(
        public string $keyPrefix,
        public string $identity,
        private string $secret,
        public int $maxPartitions = self::DEFAULT_MAX_PARTITIONS,
        public int $maxRecords = self::DEFAULT_MAX_RECORDS,
    );
}

RedisRegistryAdmin

The only way to create, check or retire a registry. The host calls it with its administration connection. The application never does.

PHP
final class RedisRegistryAdmin {
    public function __construct(
        RedisConnectionOptions $administrativeConnection,
        private readonly RedisRegistry $registry,
    );
    public function initialize(): RegistryStatus;
    public function verify(): RegistryStatus;
    public function retire(): RegistryStatus;
    public function purgeRetired(): int;
    public function inspectServer(): ServerPolicyReport;
}
  • initialize() Creates the registry, or confirms an identical one. A different identity or different ceilings are refused. Refused on a server older than Redis 7.0 and on a prefix that still holds leftovers of a retired registry.
  • verify() Checks presence, identity, schema and counters. Writes nothing.
  • retire() Deletes the registry metadata. Partition and record keys stay until purgeRetired() removes them.
  • purgeRetired() Removes the keys a retired registry left behind. Refuses to run while the registry exists. Returns the number of keys removed.
  • inspectServer() Reads maxmemory-policy and maxmemory. Needs CONFIG GET and INFO. Refuses a server older than Redis 7.0.

RedisConnectionOptions

Immutable options for one direct Redis connection. Both timeouts are required. There is no retry option and no way to turn off certificate or host name checks. The password never appears in var_dump or in an exception message.

PHP
final readonly class RedisConnectionOptions {
    public function __construct(
        public string $host,
        public float $connectTimeoutSeconds,
        public float $readWriteTimeoutSeconds,
        public int $port = 6379,
        public ?string $username = null,
        private ?string $password = null,
        public bool $tls = false,
        public ?string $caFile = null,
        public ?string $tlsServerName = null,
    );
}

RedisAcl

The Redis ACL rules the library needs, and no more. Print them and pass them to ACL SETUSER.

PHP
final class RedisAcl {
    public static function execution(RedisRegistry $registry): array;
    public static function administration(RedisRegistry $registry): array;
}
  • execution() Rules for the application user: the shipped scripts and their commands, only on keys under the registry prefix.
  • administration() The same rules plus SCAN, UNLINK, CONFIG GET and INFO, for the host.

RegistryStatus

What a registry administration call found or did: whether it created the registry, the authority watermark, and the counts against the ceilings.

PHP
final readonly class RegistryStatus {
    public function __construct(
        public bool $created,
        public int $watermarkSeconds,
        public int $watermarkMicroseconds,
        public int $partitions,
        public int $records,
        public int $maxPartitions,
        public int $maxRecords,
    );
}

ServerPolicyReport

What the server does when it runs out of memory. protectsState() is true for noeviction or no memory limit, false for a policy that can delete keys, and null when the policy could not be read.

PHP
final readonly class ServerPolicyReport {
    public function __construct(
        public ?string $evictionPolicy,
        public ?int $maxMemoryBytes,
    );
    public function protectsState(): ?bool;
}

Redis package: operate

Run these from a worker, a scheduler or an operator command, with a connection that has the administration rules. The library never calls them for you.

RedisRegistryMaintenance

Bounded cleanup of a registry. One step looks at no more than 128 candidates and no more than 128 finished records. Nothing runs it for you.

PHP
final class RedisRegistryMaintenance {
    public function __construct(
        RedisConnectionOptions $connection,
        private readonly RedisRegistry $registry,
        ?RedisBridge $bridge = null,
    );
    public function step(): array;
}
  • step() Runs one step and returns what it removed and whether more may be found. Call again until more is false.

RedisCircuitAdmin

Operator actions on circuit breaker partitions. A reset is never called by the library.

PHP
final class RedisCircuitAdmin {
    public function __construct(
        RedisConnectionOptions $connection,
        private readonly RedisRegistry $registry,
        ?RedisBridge $bridge = null,
    );
    public function reset(PolicyIdentity $identity): ?Generation;
}
  • reset() Closes the circuit, discards its history and makes every active permit stale. Returns the new generation, or null when the partition does not exist.

Redis package: backends

One backend for each policy. RedisEnvironment::for() builds them for you. Create one yourself only to pass it to ReqtideClient::withBackends(). The third constructor argument is a seam for tests: leave it out. Each backend sends one server-side script call for each step, and a command is never sent twice.

RedisRateLimitBackend

Shared token bucket, fixed window and sliding counter.

PHP
final class RedisRateLimitBackend implements RateLimitBackend, StateBackend {
    public function __construct(
        RedisConnectionOptions $connection,
        private readonly RedisRegistry $registry,
        ?RedisBridge $bridge = null,
    );
    public function capabilities(): StateBackendCapabilities;
    public function consume(
        RateLimitPolicy $policy,
        PositiveInt $cost,
        CommandId $command,
    ): AdmissionDecision;
    public function inspect(
        RateLimitPolicy $policy,
        PositiveInt $requestedCost,
    ): RateLimitInspection;
}

RedisCircuitBackend

Shared circuit breaker. Open and half-open state, active permits and count history are never reclaimed or evicted.

PHP
final class RedisCircuitBackend implements CircuitBackend, StateBackend {
    public function __construct(
        RedisConnectionOptions $connection,
        private readonly RedisRegistry $registry,
        ?RedisBridge $bridge = null,
    );
    public function capabilities(): StateBackendCapabilities;
    public function acquire(
        CircuitPolicy $policy,
        AttemptId $attempt,
        PermitOwnerId $owner,
        CommandId $command,
    ): CircuitAdmission;
    public function record(
        CircuitPolicy $policy,
        CircuitPermit $permit,
        CircuitVerdict $verdict,
        CommandId $command,
    ): FinalizationResult;
    public function abandon(
        CircuitPolicy $policy,
        CircuitPermit $permit,
        CommandId $command,
    ): FinalizationResult;
}

RedisConcurrencyBackend

Shared bulkhead with leases. Active owners are never evicted.

PHP
final class RedisConcurrencyBackend implements ConcurrencyBackend, StateBackend {
    public function __construct(
        RedisConnectionOptions $connection,
        private readonly RedisRegistry $registry,
        ?RedisBridge $bridge = null,
    );
    public function capabilities(): StateBackendCapabilities;
    public function acquire(
        ConcurrencyPolicy $policy,
        PermitOwnerId $owner,
        CommandId $command,
    ): ConcurrencyAdmission;
    public function release(
        ConcurrencyPolicy $policy,
        ConcurrencyPermit $permit,
        CommandId $command,
    ): FinalizationResult;
}

RedisRetryBudgetBackend

Shared retry budget. A balance is never reclaimed or evicted.

PHP
final class RedisRetryBudgetBackend implements RetryBudgetBackend, StateBackend {
    public function __construct(
        RedisConnectionOptions $connection,
        private readonly RedisRegistry $registry,
        ?RedisBridge $bridge = null,
    );
    public function capabilities(): StateBackendCapabilities;
    public function inspect(RetryBudgetPolicy $policy): RetryBudgetInspection;
    public function reserve(
        RetryBudgetPolicy $policy,
        AttemptId $attempt,
        PermitOwnerId $owner,
        CommandId $command,
    ): RetryReservationDecision;
    public function commit(
        RetryBudgetPolicy $policy,
        RetryBudgetReservation $reservation,
        CommandId $command,
    ): RetryBudgetFinalizationResult;
    public function release(
        RetryBudgetPolicy $policy,
        RetryBudgetReservation $reservation,
        CommandId $command,
    ): RetryBudgetFinalizationResult;
}

RedisCooldownBackend

Shared cooldown. An extension never shortens an existing pause.

PHP
final class RedisCooldownBackend implements CooldownBackend, StateBackend {
    public function __construct(
        RedisConnectionOptions $connection,
        private readonly RedisRegistry $registry,
        ?RedisBridge $bridge = null,
    );
    public function capabilities(): StateBackendCapabilities;
    public function inspect(CooldownPolicy $policy): CooldownDecision;
    public function extend(
        CooldownPolicy $policy,
        Duration $extension,
        CommandId $command,
    ): CooldownDecision;
}

Guzzle package

These classes belong to the separate package enaxon/reqtide-guzzle, namespace Enaxon\Reqtide\Guzzle. It is installed as a separate package. See Guzzle requests with Reqtide. A 4xx or 5xx status is a response, never an exception.

ReqtideClient

The entry point. It owns its cURL handlers and runs each operation, with all its attempts, through one compiled pipeline.

PHP
final class ReqtideClient {
    public static function inMemory(
        PipelineBuilder $pipeline,
        ?GuzzleConfiguration $configuration = null,
        ?EventSink $events = null,
    ): self;
    public static function withBackends(
        PipelineBuilder $pipeline,
        ?RateLimitBackend $rateLimit = null,
        ?CircuitBackend $circuit = null,
        ?ConcurrencyBackend $concurrency = null,
        ?RetryBudgetBackend $retryBudget = null,
        ?CooldownBackend $cooldown = null,
        ?GuzzleConfiguration $configuration = null,
        ?EventSink $events = null,
    ): self;
    public function uri(UriInterface|string $reference): UriInterface;
    public function send(
        HttpOperation $operation,
        Duration $timeout,
        ?Cancellation $cancellation = null,
    ): ResponseInterface;
    public function sendDetailed(
        HttpOperation $operation,
        Duration $timeout,
        ?Cancellation $cancellation = null,
    ): ExecutionCompletion;
    public function sendAsync(
        HttpOperation $operation,
        Duration $timeout,
        ?Cancellation $cancellation = null,
    ): PromiseInterface;
    public function explain(bool $async = false): PipelineExplanation;
}
  • inMemory() A client with process-local state.
  • withBackends() A client whose state lives in the backends you pass, for example the Redis backends. Pass a backend for every policy the pipeline configures.
  • uri() Resolves a reference against the base URI (RFC 3986). The base URI is not a security boundary: an absolute reference replaces the host.
  • send() Runs the operation and returns the response, or throws the failure of the operation. The timeout covers every attempt and every wait.
  • sendDetailed() Returns an ExecutionCompletion with the summary of attempts and decisions instead of throwing.
  • sendAsync() Returns a lazy promise. The operation runs when you call wait(), one operation at a time.
  • explain() Describes the compiled pipeline of the synchronous runtime, or of the promise runtime with true. No transfer, no state change.

GuzzleConfiguration

The options of the client: the classifier, a short list of Guzzle client defaults and an optional base URI. Any other client default is refused by name.

PHP
final readonly class GuzzleConfiguration {
    public const ALLOWED_CLIENT_DEFAULTS = array (
  0 => 'headers',
  1 => 'auth',
  2 => 'proxy',
  3 => 'verify',
  4 => 'cert',
  5 => 'ssl_key',
  6 => 'crypto_method',
  7 => 'force_ip_resolve',
  8 => 'version',
  9 => 'decode_content',
);
    public HttpOutcomeClassifier $classifier;
    public ?UriInterface $baseUri;
    public function __construct(
        ?HttpOutcomeClassifier $classifier = null,
        public array $clientDefaults = [],
        UriInterface|string|null $baseUri = null,
        public array $credentialHeaders = [],
    );
}

HttpOperation

One logical HTTP operation: an absolute request and your explicit statement of whether and how it may be sent again. Nothing is guessed from the method or a header.

PHP
final readonly class HttpOperation {
    public const DEFAULT_MAX_VERIFIED_BODY_BYTES = 16777216;
    public ?Closure $bodyFactory;
    public function forClient(GuzzleConfiguration $configuration): self;
    public static function read(
        RequestInterface $request,
        string $operationGroup,
        Duration $validFor,
        array $retryableOutcomeCodes = [],
        ?Closure $bodyFactory = null,
        ?Clock $clock = null,
        ?string $credentialKey = null,
        BodyIntegrity $bodyIntegrity = BodyIntegrity::Verified,
        int $maxVerifiedBodyBytes = self::DEFAULT_MAX_VERIFIED_BODY_BYTES,
    ): self;
    public static function idempotentMutation(
        RequestInterface $request,
        string $operationGroup,
        string $identityValue,
        string $identityScope,
        string $duplicateHandling,
        Duration $validFor,
        ?Duration $retention = null,
        ?string $identityHeader = 'Idempotency-Key',
        array $retryableOutcomeCodes = [],
        ?Closure $bodyFactory = null,
        ?Clock $clock = null,
        ?string $credentialKey = null,
        BodyIntegrity $bodyIntegrity = BodyIntegrity::Verified,
        int $maxVerifiedBodyBytes = self::DEFAULT_MAX_VERIFIED_BODY_BYTES,
    ): self;
    public static function single(
        RequestInterface $request,
        string $operationGroup,
        ?string $credentialKey = null,
    ): self;
    public function metadata(): OperationMetadata;
}
  • read() For GET and HEAD. You state that repeating the request has no side effect, for how long, and which outcomes may be retried.
  • idempotentMutation() For POST, PUT, PATCH or DELETE that the server deduplicates by an identity. The same body and the same identity are sent on every attempt, and the identity is checked before each one.
  • single() Sent once, never repeated. Core refuses it in a pipeline that retries more than once.
  • metadata() The bounded facts passed to the pipeline: the safety statement and the descriptor attributes.

HttpOutcomeClassifier

Decides which statuses and cURL errors are retried, count against a circuit, may fall back or set a cooldown. Every method returns a new instance.

PHP
final readonly class HttpOutcomeClassifier implements OutcomeClassifier {
    public static function starter(?Clock $clock = null): self;
    public static function empty(?Clock $clock = null): self;
    public function withRetryableStatuses(int ...$statuses): self;
    public function withCircuitFailureStatuses(int ...$statuses): self;
    public function withCircuitNeutralStatuses(int ...$statuses): self;
    public function withFallbackStatuses(int ...$statuses): self;
    public function withRetryAfterStatuses(int ...$statuses): self;
    public function withRetryableTransportErrnos(int ...$errnos): self;
    public function retryableOutcomeCodes(): array;
    public function classify(
        Outcome $outcome,
        OperationMetadata $metadata,
    ): OutcomeClassification;
}
  • starter() A documented starting set. It is not a recommendation for every API.
  • empty() Retries and falls back on nothing and reads no Retry-After.
  • retryableOutcomeCodes() The reason codes that can be retried. Pass them to read() and idempotentMutation(). An empty list never retries.

RetryAfter

A strict reader of Retry-After: seconds or an HTTP date. A value that cannot be read gives no wait.

PHP
final class RetryAfter {
    public const MAX_SECONDS = 9007199254;
    public static function read(
        array $lines,
        DateTimeImmutable $now,
    ): RetryAfterReading;
}
  • read() Reads the header lines against the given wall time. The greatest valid delay wins.

RetryAfterReading

What the Retry-After lines of one response say. A null delay means no lower bound, never zero.

PHP
final readonly class RetryAfterReading {
    public function __construct(
        public ?Duration $delay,
        public int $validLines,
        public int $rejectedLines,
        public bool $outOfRange,
    );
}