poagen / client-generator
OpenAPI-driven PHP client generator producing typed, extensible clients.
Requires
- php: ^8.4
- devizzent/cebe-php-openapi: ^1.1
- nette/php-generator: ^4.2
- poagen/client-runtime: ^0.1
- psr/http-message: ^2.0
- sebastian/diff: ^6.0 || ^7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.92
- infection/infection: ^0.34.1
- nyholm/psr7: ^1.8
- phpstan/phpstan: ^2.1
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^12.1
- ramsey/uuid: ^4.7
- rector/rector: ^2.3
- shipmonk/dead-code-detector: ^1.0
- symfony/uid: ^7.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
poagen
OpenAPI-driven PHP client generator producing typed, extensible clients.
Documentation · Quickstart
poagen reads an OpenAPI 3.0 or 3.1 document and emits a PHP 8.4 HTTP client. The generated tree is disposable: every run rewrites it whole, and your own code lives beside it. The output is deterministic, so a diff after regeneration shows only real changes.
Why poagen
- Typed. Exhaustive PHPStan annotations, backed enums, readonly models, and one method per operation.
- Extensible. Guidance lives in a typed
Profileand in attributes on your own value objects, never in an edit to generated code. - PSR-aligned. The emitted client depends on PSR-18, PSR-17 and PSR-7, so Guzzle, Symfony HTTP Client, or anything else that implements them plugs in.
- Fail loud. An unsupported construct stops the run by name. poagen never drops an operation and calls the result a success.
- Proven on real documents. Petstore, DigitalOcean, GitHub, and kilbil’s loyalty API all generate and pass their own gates.
Install
The runtime goes in require, because generated code runs in production. The generator goes in require-dev, because it only runs while you generate:
composer require poagen/client-runtime composer require --dev poagen/client-generator
Quickstart
Write a profile:
<?php declare(strict_types=1); use Poagen\Generator\Generator\Profile; return new Profile( schema: __DIR__ . '/schemas/petstore-3.0.yaml', output: __DIR__ . '/generated', namespace: 'App\\Petstore', );
Generate:
vendor/bin/poagen generate poagen.php
Use the client:
use App\Petstore\Generated\ApiClient; use App\Petstore\Generated\ApiException; use Poagen\Runtime\Http\HttpTransport; $client = new ApiClient( new HttpTransport(ApiClient::SERVER_1, ApiException::class), apiKey: $apiKey, petstoreAuth: $token, ); $response = $client->pet()->getPetById(1); echo $response->body?->name;
Every operation returns an Envelope carrying ->status, ->body and the PSR-7 ->response. A declared non-2xx status is a value to inspect, not an exception.
Documentation
- Quickstart takes you from an empty project to a first response.
- Installation covers requirements and versions.
- Profile is the configuration reference.
- Generated code explains models, enums, clients and envelopes.
- Formats covers value objects, codecs and date-time zones.
- CLI covers generate, validate, dry runs and diffs.
- Examples shows the four real documents.
- Status lists what poagen refuses by name.
Contributing
See CONTRIBUTING.md. The project’s decisions and specs live under truth/, next to the code they describe.
License
MIT.