Profile

The typed Profile is the whole configuration surface: schema, output, namespace, and the guidance that shapes names and types.

poagen.php returns a Poagen\Generator\Generator\Profile. The profile is read as data, never executed, and every entry is validated against the document before anything is written.

Minimal 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',
);

namespace is the root of the emitted classes. Generated files land under namespace\Generated\, so App\Petstore becomes App\Petstore\Generated\ApiClient and App\Petstore\Generated\Model\Pet.

Fields

FieldWhat it answers
schemaPath to the OpenAPI YAML or JSON document.
outputDirectory poagen owns and rewrites.
namespaceRoot namespace for emitted classes.
formatsFormat string to a value object or codec, document-wide.
propertyFormatsFormat binding for one property of one model.
discoverInNamespaces scanned for #[BindsFormat] attributes.
methodRenamesOperation id to the method name you want.
tagRenamesOpenAPI tag to the sub-client name you want.
propertyRenamesWire property to the PHP property name.
renamesSchema component to the model class name.
xmlElementsRoot, item and nested-container elements for XML request bodies.
dateTimeZoneThe zone a document means when it sends a date-time without an offset.
securitySchemesSchemes a security requirement names but the document never declares.
inlineModelsNames, and optional merges, for synthesised inline payload models.
responseMapsOperations that share one response-map declaration.
skipOperations or components to leave out, each with a reason.

The constructor and every field are documented in src/Generator/Profile.php. That file is the reference; this page is the map.

Naming

OpenAPI often names operations in a way no algorithm can split (getclientfullbalance), and tags may be Cyrillic. poagen will not guess: it keeps the vendor spelling, reports it on stderr, and lets the profile answer.

methodRenames: [
'getclientfullbalance' => 'getClientFullBalance',
],
tagRenames: [
'Клиенты' => 'Customers',
],

A rename that names an operation, tag or property the document does not carry fails the run. A rename that collides with another name fails too.

Types

formats and propertyFormats bind a format to a class. formats applies wherever the format appears; propertyFormats applies to one property of one model, keyed by the emitted class name and the wire property name.

formats: [
'uuid' => RamseyUuidCodec::class,
],
propertyFormats: [
'SearchClientRequestContentGoodsDataItem.price' => Money::class,
],

See Formats and value objects for the binding shapes and the date-time zone handling.

XML request bodies

OpenAPI names the elements of a property, but not the document root or the repeated element of a bare array. The document may also nest a container one level deeper than the schema suggests. xmlElements answers all three:

xmlElements: [
'goodsxml' => 'goods',
'goodsxml.item' => 'good',
'goodsxml.item.inside' => 'good',
],

A binding naming a property absent from the item schema fails the run.

Leaving work out

skip is the explicit way to accept an operation poagen cannot represent. Each entry carries a reason, and the run reports it:

skip: [
'uploadForm' => 'multipart/form-data request body',
],

Skipping is a decision, not a silent fallback. A skipped operation still appears in the run report.