PHP
The PHP SDK is an idiomatic, Composer-installable client emitted from the same GlottoIR as every
other target. Files declare strict_types=1, models are typed-property objects with fromJson,
and PHPDoc is generated from the operation prose in the spec.
Manual pages expose typed items, the full response and explicit continuation. A trailing RequestOptions controls headers, deadlines, retries, extra parameters and cooperative cancellation. Binary operations return an owned Core\BinaryDownload with bounded readAll, pipe, response metadata and close; foreach closes on break or error. See pagination, request retries and controls, streaming, and file transfers.
Quickstart
composer require your-org/petstore
<?php
require 'vendor/autoload.php';
$client = new Petstore\Client(token: '<token>');
$result = $client->pets->get('...');
var_dump($result);
Resource objects
Operations are camelCase methods on resource objects — $client->pets->get($id),
$client->pets->photos->add(...) — the Stripe-PHP service-accessor shape, rather than flat
snake_case methods.
PSR-18 transport
The client sends requests through an injectable PSR-18 HTTP client (Guzzle by default), so the
transport is swappable and mockable — the PHP-ecosystem standard, the analogue of C#’s injectable
HttpClient.
Typed errors
Non-2xx responses throw ApiError, an \Exception hierarchy carrying the parsed, typed error
body; discriminated-union response bodies resolve to the right concrete type. See
Errors.
Pagination
List methods return a lazy iterator (foreach) that walks every page, advancing the cursor for
you. See Pagination.
Retries & backoff
Transient failures (5xx, 429, transport errors) retry with exponential backoff and jitter,
configurable per client. See Retries & timeouts, Streaming,
and Authentication.
Unknown response fields
Your API can add a response field without it being a breaking change — but a generated class has no promoted property to put it in, and its fromJson factory reads only the fields it declares. The generated
models keep it instead: a field the SDK wasn’t generated from is retained on decode, readable
through an accessor, and written back out when the model is re-serialized.
$pet = $client->pets->createPet($body);
// A field your API started returning after this SDK was generated.
$species = $pet->extraFields()['species'] ?? null;
// Re-encoding preserves it — a read-modify-write never silently drops it.
$json = json_encode($pet);
extraFields() returns an array<string, mixed>, so nested objects and lists survive intact, and
retention is recursive.
One caveat from PHP’s JSON decoder: json_decode($body, true) represents an empty JSON object and
an empty array identically, so an unknown field whose value is {} round-trips as []. Every
other shape — nested objects, lists, scalars, null — round-trips unchanged.
Your existing construction still compiles: the storage is a private field assigned after construction, so the promoted constructor is untouched.
The retained fields are read-only by design. To send a field your spec doesn’t model yet, use the per-call extra-body escape hatch rather than writing to the retained bag.