Skip to content

Support PHP 8.5 and rework retries and error handling (0.1.0) - #1

Merged
jschaedl merged 2 commits into
mainfrom
feature/php-8.5-and-retry-overhaul
Oct 25, 2026
Merged

jschaedl merged 2 commits into
mainfrom
feature/php-8.5-and-retry-overhaul

Conversation

@jschaedl

Copy link
Copy Markdown
Owner

Summary

Makes the library installable on PHP 8.5 and fixes the retry and error handling so it is safe for non-idempotent requests. Released as 0.1.0; breaking changes are listed in UPGRADE-0.1.md.

PHP and tooling

  • PHP ~8.2 to ~8.5; CI tests the whole range
  • PHPUnit 11 (tests moved to attributes), PHPStan 2 (level max, type coverage 99%, empty baseline), Rector 2, current php-cs-fixer rule sets
  • Dropped the abandoned azjezz/psl and the unused symplify/phpstan-rules and spatie/phpunit-snapshot-assertions

Retries

  • GenericRetryStrategy behind a new RetryStrategyInterface; RetryTrait removed
  • 5xx and network errors are retried for idempotent methods only, because a resent POST can create a second order or invoice. 429 is retried for every method.
  • Retry-After (seconds or HTTP-date) is honoured; if it asks for longer than maxDelay, the request is not retried
  • RetryClient gets a maxDuration deadline (waits included) and an injectable clock instead of usleep
  • maxRetries now counts retries, not attempts

Errors

  • Client throws ClientException/ServerException on 4xx/5xx (HTTP status as code, request and response attached) and TransportException on network failures (cause chained); all implement ApiExceptionInterface
  • Messages never contain response bodies or query strings
  • Response handlers run before the status check, so they can turn an expected 404 into a domain exception
  • throwOnErrorStatus: false keeps the old behaviour

Bodies

  • Request bodies can be array|string|null; a string is sent as-is
  • Encoders set their own Content-Type; a request can bring its own encoder (new FormRequestBodyEncoder)
  • Responses keep rawBody() and decode lazily; decode failures throw DecodingException

Testing

  • Testing\MockClient (queued responses, recorded requests) and Clock\MockClock

Verification

  • make tests: unit 60, integration 13, e2e 2 (real Symfony + Guzzle clients against httpbin)
  • Non-network tests also pass on PHP 8.2, 8.3 and 8.4 (Docker php:*-cli)
  • make cs-check, make analyse and rector --dry-run are clean

jschaedl and others added 2 commits October 25, 2026 12:03
- PHP 8.2 to 8.5; PHPUnit 11 (attributes), PHPStan 2, Rector 2,
  current php-cs-fixer rule sets; CI matrix 8.2-8.5
- GenericRetryStrategy: 5xx and network errors are retried for
  idempotent methods only, 429 for every method; Retry-After is
  honoured; RetryClient gains a maxDuration deadline and an
  injectable clock; RetryTrait is removed
- Client throws ClientException/ServerException on 4xx/5xx and
  TransportException on network failures, with status code and cause;
  messages never contain response bodies or query strings
- Request bodies can be strings; encoders set their Content-Type and
  a request can bring its own encoder (FormRequestBodyEncoder)
- Responses keep the raw body and decode lazily
- Testing\MockClient and Clock\MockClock for consumer tests
- README sections filled in, UPGRADE-0.1.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
paratest 7.8.0 calls a PHPUnit method that 11.5.50 does not have, so
every worker crashed; psr/http-factory 1.0 and nyholm/psr7 < 1.8.2
declare implicitly nullable parameters, deprecated since PHP 8.4,
which failOnDeprecation turns into failures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jschaedl jschaedl self-assigned this Oct 25, 2026
@jschaedl
jschaedl merged commit fb2e4bd into main Oct 25, 2026
20 checks passed
@jschaedl
jschaedl deleted the feature/php-8.5-and-retry-overhaul branch October 25, 2026 11:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant