HTTP Retry Request Planner
Builds retry timing and stop rules for outbound HTTP requests.
Function signature
ogPlanHttpRetryRequest(request_state = array(), policy = array())
Categories
- Performance
Parameters
request_stateRequest state with attempt, status_code, method, and retry_after keys. Recognized keys: `attempt`, `idempotent`, `method`, `retry_after`, `status_code`.policyRetry policy overrides. Recognized keys: `base_delay_seconds`, `max_attempts`, `max_delay_seconds`, `retryable_statuses`.Return value
Short public-safe status message.
- should_retry
- delay_seconds
- attempt
- max_attempts
- status_code
- method
- reason
Compatibility
Existing function name and call order preserved; metadata signature corrected to source.
Minimum PHP version: 7.4
Security notes
Validate request method, identity, permissions, policy arrays, URLs, signatures, and caller-owned allowlists before use; keep secrets and internal paths out of public output.
Code
<?php
/*
* Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
* Free for personal and internal use. Paid project use requires visible credit
* to Jeffery L. Paris. Corporate use requires a paid license fee unless a
* separate written license states otherwise.
*/
/**
* Builds a safe retry decision for outbound HTTP requests.
*
* The helper respects idempotency, Retry-After values, retryable status
* codes, capped exponential backoff, and a maximum attempt limit.
*
* @param array $request_state Request state with attempt, status_code, method, and retry_after keys.
* @param array $policy Retry policy overrides.
* @return array Retry decision with delay and reason data.
*/
function ogPlanHttpRetryRequest($request_state = array(), $policy = array()) {
$result = array(
'success' => false,
'message' => '',
'data' => array()
);
if (!is_array($request_state)) {
$result['message'] = 'Request state must be an array.';
return $result;
}
if (!is_array($policy)) {
$policy = array();
}
$attempt = 0;
if (!empty($request_state['attempt'])) {
$attempt = (int)$request_state['attempt'];
}
if ($attempt < 0) {
$attempt = 0;
}
$status_code = 0;
if (!empty($request_state['status_code'])) {
$status_code = (int)$request_state['status_code'];
}
$method = 'GET';
if (!empty($request_state['method'])) {
$method = strtoupper(trim((string)$request_state['method']));
}
$max_attempts = 3;
if (!empty($policy['max_attempts'])) {
$max_attempts = (int)$policy['max_attempts'];
}
if ($max_attempts < 1) {
$max_attempts = 1;
}
$base_delay = 2;
if (!empty($policy['base_delay_seconds'])) {
$base_delay = (int)$policy['base_delay_seconds'];
}
if ($base_delay < 1) {
$base_delay = 1;
}
$max_delay = 300;
if (!empty($policy['max_delay_seconds'])) {
$max_delay = (int)$policy['max_delay_seconds'];
}
if ($max_delay < $base_delay) {
$max_delay = $base_delay;
}
$retryable_statuses = array(408, 425, 429, 500, 502, 503, 504);
if (!empty($policy['retryable_statuses']) && is_array($policy['retryable_statuses'])) {
$retryable_statuses = array();
foreach ($policy['retryable_statuses'] as $status) {
$retryable_statuses[] = (int)$status;
}
}
$idempotent_methods = array('GET', 'HEAD', 'PUT', 'DELETE', 'OPTIONS');
$is_idempotent = in_array($method, $idempotent_methods, true);
if (!empty($request_state['idempotent'])) {
$is_idempotent = true;
}
$should_retry = false;
$reason = 'Status is not retryable.';
if (in_array($status_code, $retryable_statuses, true)) {
$should_retry = true;
$reason = 'Status is retryable.';
}
if (!$is_idempotent) {
$should_retry = false;
$reason = 'Request method is not idempotent.';
}
if ($attempt >= $max_attempts) {
$should_retry = false;
$reason = 'Maximum attempts reached.';
}
$delay = 0;
if ($should_retry) {
if (!empty($request_state['retry_after'])) {
$retry_after = trim((string)$request_state['retry_after']);
if (ctype_digit($retry_after)) {
$delay = (int)$retry_after;
} else {
$retry_time = strtotime($retry_after);
if (!empty($retry_time)) {
$delay = $retry_time - time();
}
}
}
if ($delay <= 0) {
$delay = $base_delay * (int)pow(2, $attempt);
}
if ($delay > $max_delay) {
$delay = $max_delay;
}
}
$result['success'] = true;
$result['message'] = 'HTTP retry decision built.';
$result['data'] = array(
'should_retry' => $should_retry,
'delay_seconds' => $delay,
'attempt' => $attempt,
'max_attempts' => $max_attempts,
'status_code' => $status_code,
'method' => $method,
'reason' => $reason
);
return $result;
}