the categories a live provider request can end in */ public static function categories(): array { return [FailureCatalog::INVALID_CREDENTIALS, FailureCatalog::PERMISSION, FailureCatalog::RATE_LIMITED, FailureCatalog::QUOTA, FailureCatalog::SERVER_ERROR, FailureCatalog::TIMEOUT, FailureCatalog::STREAM_INTERRUPTED, FailureCatalog::REQUEST_REFUSED, FailureCatalog::NO_ANSWER, FailureCatalog::INTERNAL]; } /** @return bool true when the option was written */ public function record(int $connectionId, string $category, ?int $http, string $billing): bool { if ($connectionId < 1 || !in_array($category, self::categories(), true) || !in_array($billing, FailureCatalog::billingStates(), true) || ($http !== null && ($http < 100 || $http > 599))) { return false; } $now = $this->clock->now(); $cells = $this->cells($connectionId, $now); $old = $cells[$category] ?? null; if ($old !== null && $old['http'] === $http && $old['billing'] === $billing && $now - $old['last'] < self::MIN_REWRITE) { return false; } $cells[$category] = ['first' => $old['first'] ?? $now, 'last' => $now, 'http' => $http, 'billing' => $billing]; // Bound the size: keep the newest cells that fit (ten small cells are far below the limit; this is a guard, not a usual path). uasort($cells, static fn(array $a, array $b): int => $b['last'] <=> $a['last']); // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode -- measures the size of plain integers and fixed codes; this class stays free of WordPress functions while (strlen((string) json_encode($cells)) > self::MAX_BYTES && count($cells) > 1) { array_pop($cells); } $this->options->set(self::OPTION_PREFIX . $connectionId, $cells); return true; } /** @return list newest first, within the retention period */ public function recent(int $connectionId): array { $rows = []; foreach ($this->cells($connectionId, $this->clock->now()) as $category => $cell) { $rows[] = ['category' => $category, 'http' => $cell['http'], 'billing' => $cell['billing'], 'first' => $cell['first'], 'last' => $cell['last']]; } usort($rows, static fn(array $a, array $b): int => $b['last'] <=> $a['last']); return $rows; } public function forget(int $connectionId): void { if ($connectionId > 0 && $this->options->get(self::OPTION_PREFIX . $connectionId, []) !== []) { $this->options->set(self::OPTION_PREFIX . $connectionId, []); } } /** @return array only well-formed, unexpired cells */ private function cells(int $connectionId, int $now): array { $stored = $this->options->get(self::OPTION_PREFIX . $connectionId, []); $out = []; foreach (is_array($stored) ? $stored : [] as $category => $cell) { if (!is_string($category) || !in_array($category, self::categories(), true) || !is_array($cell)) { continue; } $first = $cell['first'] ?? null; $last = $cell['last'] ?? null; $http = $cell['http'] ?? null; $billing = $cell['billing'] ?? null; if (!is_int($first) || !is_int($last) || $last < $first || $now - $last > self::RETENTION || ($http !== null && (!is_int($http) || $http < 100 || $http > 599)) || !is_string($billing) || !in_array($billing, FailureCatalog::billingStates(), true)) { continue; } $out[$category] = ['first' => max($first, $now - self::RETENTION), 'last' => $last, 'http' => $http, 'billing' => $billing]; } return $out; } }