NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist · #313 most downloaded on Packagist
Generate interactive documentation for your RESTful API using PHP attributes (preferred) or PHPDoc annotations
Last release 2 days ago
06 Oct 2026
Release timing varies
gaps range from 8 days to 2 months
Some releases are documented
notes for 19 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
15 years old
219 releases · first in 2012
…, header: , link: and example: now trigger a deprecation, and are removed in 8.0 . Only their use as a component key is deprecated: response: 404 on a…
Without -o, bin/openapi writes the document to stdout, and its warnings went there too, so openapi src > openapi.yaml put them into the file ahead of the YAML. Diagnostics now go to stderr, and stdout carries only the document.
A script that reads warnings from stdout has to read stderr instead. With -o, warnings used to be the only thing on stdout, so a check like openapi src -o openapi.yaml | grep -q warning && exit 1 worked. It now finds nothing and passes. Read both streams with 2>&1.
security: [] is keptAn operation opts out of a root security requirement with security: []. Spec and hybrid modes dropped the empty list as if it were unset, so the operation documented as requiring the root's authentication. It is now emitted. Classic mode was not affected.
component: key on every reusable attributeEach reusable OpenApi\Spec attribute names the key it is filed under with component:, in place of a field spelled after its own type. PathItem and MediaType take it too, and can now be reused: a keyed PathItem is a components.pathItems entry from 3.1, and a keyed MediaType a components.mediaTypes entry from 3.2. See Components.
The old spellings keep working and produce the same document. Used as a component key, schema:, parameter:, request:, securityScheme:, response:, header:, link: and example: now trigger a deprecation, and are removed in 8.0. Only their use as a component key is deprecated: response: 404 on a nested response is the nesting key and stays. Classic attributes report nothing.
A schema's title is no longer used as its component key; a schema with no key is reported.
Builder::withSpecification() hands the assembled Specification to a callable after the scan and before resolution. What it adds is resolved, augmented and compiled with the scanned sources, so a $ref in a contribution resolves. That makes it the place for metadata with nothing to scan: an entity registry, a serializer's configuration, another library's attributes. See Extension points.
Two operations on the same path and method, or two schemas with the same key, used to be settled by the compiler, which kept whichever it wrote last and said nothing. A merger pass now applies one rule to every root collection: the later entry wins, and the collision is reported with both locations. Builder::withMergers() takes your own mergers ahead of it, and getMeta()/setMeta() on every attribute lets a package mark its contributions so its merger can recognise them.
One output change: for two PathItems on the same path, the later one now wins, as everything else does. It used to be the first.
Result now also carries what the spec pipeline logged during the build, not only the compiler's diagnostics.
An attribute that matches more than one sibling on the same target, such as a Header stacked beside two Responses, now says what to do about it: nest it in the one it belongs to, or give it a component and reference it from each.
Full Changelog: 6.11.0...6.12.0
One column per quarter.
Docblock annotations are deprecated, and now say so
Parsing a docblock annotation triggers a runtime deprecation, once per Generator run and only
when an annotation is actually parsed — a project that has moved to attributes still has
docblocks everywhere and stays quiet.
Most people will see nothing. trigger_deprecation() is suppressed by design, so the notice
surfaces only where a deprecation handler is listening for one. It is a heads-up, not a change
of behaviour.
The README has recommended attributes over annotations since 4.8; the code now agrees. Docblock
support is marked @deprecated and removed in 8.0 — it keeps working unchanged through v7.
ROADMAP.md said classic code is marked in v6 and removed in v7, and the markers in src/ did
not say either. The rule is now written down — a marker names the version that removes the
thing, and nothing is removed before the version its marker names — and every marker in the
tree follows it.
Where that lands: anything classic depends on goes when classic does, including the sentinels
Generator::UNDEFINED and Generator::isDefault(), which classic processors and hand-built
annotation objects both use.
Specification::buildPathItemHierarchy() answers "which PathItems govern this class" — the
walk that resolves a controller's prefix, tags and security through its ancestors. It is what
the augmenter now uses in the several places that each had their own copy, and it is available
to anything else built on a Specification. Resolving path-level output no longer re-walks the
chain for every operation.
The pipeline also has a Why spec attributes?
page, leading with the thing classic attributes cannot do at all — building a document from
values, with nothing to scan.
Full Changelog: 6.10.0...6.11.0
Symfony 6.4 LTS is supported again
symfony/console moves from ^7.4 || ^8.0 to ^6.4 || ^7.0 || ^8.0, so applications on the
6.4 LTS can install current swagger-php instead of being held back at 6.0.6. Only the CLI uses
the component — the library core, the analysers, the annotations and the processors never
touch it — so the narrow requirement was reaching consumers who never run bin/openapi.
Raised by @garak in #2180 and fixed by @yceruto in #2196, which also rebuilds the command on
configure()/execute() in place of the 7.4-only attribute syntax. GenerateInput keeps
every public property it had and gains fromInput().
additionalProperties: false as a schema by @DerManoMann in #2205No effect on generated output or public API.
Full Changelog: 6.9.0...6.10.0
TypeMapper keeps a deprecated subclass at the old name, since a custom type resolver reaches it through AbstractTypeResolver . The other two have no s…
Classic can now express the parts of OpenAPI 3.1 it always claimed to model: the full Header Object, a parameter's content, mutualTLS, the Info Object's summary, and the ten JSON Schema keywords 3.1 added.
src/Annotations and src/Attributes are closed to new capabilities, but an OpenAPI construct classic claims to model and cannot express is a defect rather than a missing feature, and v6 is the last major where classic is the primary API. A field-by-field diff of every classic annotation against the 3.1 object tables found eight such gaps; this release closes seven of them.
3.0 documents lose two keywords they should never have carried. contentMediaType and contentEncoding were emitted into 3.0 output, where neither keyword exists — the 3.0 branch of @OA\Schema stripped only examples and const. They are dropped now, together with the other 3.1-only keywords:
Attachment:
type: string
- contentMediaType: image/png
- contentEncoding: base64Nothing changes for 3.1 and 3.2.
The spec pipeline emits trait members in use order. Each trait used to be prepended separately, so two traits came out in reverse order and three reversed completely — declaring T1, T2, T3 emitted p3, p2, p1. Classic and hybrid were always right. Key order carries no meaning in OpenAPI, so no document was invalid, but regenerated files will diff.
Three classes moved, all spec-pipeline internals:
| Was | Is |
|---|---|
OpenApi\Utils\CollectingLogger |
OpenApi\Loggers\CollectingLogger |
OpenApi\Utils\SpecificationWalker |
OpenApi\Specification\Walker |
OpenApi\Utils\TypeMapper |
OpenApi\Type\TypeMapper |
TypeMapper keeps a deprecated subclass at the old name, since a custom type resolver reaches it through AbstractTypeResolver. The other two have no shim.
The Header Object is complete. @OA\Header and #[OA\Header] gain style, explode, example, examples and content, so a header can finally carry an example or a media type instead of a bare schema. schema and content are mutually exclusive and saying both now warns.
A parameter's content accepts a plain @OA\MediaType. The @OA\JsonContent / @OA\XmlContent shortcuts already worked; the verbose form was dropped silently, so a parameter written that way produced no content at all.
mutualTLS is a valid @OA\SecurityScheme type. The enum rejected it outright. In 3.0 documents, where the type does not exist, the scheme warns and is omitted.
@OA\Info gains summary. Dropped silently from 3.0 output, matching how License::$identifier is handled.
Ten JSON Schema keywords arrive on every schema annotation — @OA\Schema, @OA\Property, @OA\Items, @OA\JsonContent, @OA\XmlContent and @OA\AdditionalProperties:
if, then, else, prefixItems, dependentRequired, dependentSchemas, minContains, maxContains, unevaluatedItems, contentSchema
Classic had adopted the 3.1 keywords partway — contains without minContains, unevaluatedProperties without unevaluatedItems — so the families were half-expressible. contentSchema was missing from the spec pipeline too and is added there as well.
#[OA\Schema(
type: 'array',
prefixItems: [new OA\Schema(type: 'string'), new OA\Schema(type: 'integer')],
contains: new OA\Schema(type: 'string'),
minContains: 1,
)]A 3.1 array schema described by prefixItems or contains no longer demands items; 3.0 still warns, since the keywords do not survive there.
allOf named its parent by class-string emitted the same $ref twice in the spec and hybrid pipelines — dedup compared the raw values, and ran before class-strings resolved (#2185)@OA\JsonContent / @OA\XmlContent through the bridge rather than by running two classic processors over a mapping it already knows (#2184)Generating 3.0 now warns for prefixItems, unevaluatedProperties, unevaluatedItems and if/then/else, with the same message text the spec compiler uses. contains, minContains, maxContains, patternProperties, propertyNames, dependentRequired, dependentSchemas, contentSchema, contentMediaType and contentEncoding drop silently.
A root @OA\Response whose component key looks like a status code (response: "404") is almost always a response meant for an operation, and now says so.
Every reference page is generated through one set of section classes, so the annotation, attribute and spec-attribute pages stay in the same shape. CONTRIBUTING.md states the commit subject format and what a Changes entry is for.
Full Changelog: 6.8.1...6.9.0
Corrects the schema examples handling introduced in 6.8.0.
Corrects the schema examples handling introduced in 6.8.0.
6.8.0 changed generated output and the release notes did not say so. @OA\Examples nested under an @OA\Schema or @OA\Property used to emit a keyed map, which no validator accepts. It now emits the list of values JSON Schema calls for:
YoYo:
examples:
- yo:
- summary: 'the yo'
- value: YoYo
+ - YoYosummary, description and externalValue have nowhere to go in a list and are dropped, and an example carrying no value contributes nothing. For Example Objects use a media type, parameter or header — their examples is a map and keeps every field.
@OA\Examples under a schema no longer reports a missing example key-field or a missing summary; neither reaches the output (#2177)summary is no longer required on @OA\Examples anywhere — the Example Object has no required fields in the specificationexamples now accepts plain values, in attributes and docblocks alike, and keeps the order you write when mixed with @OA\Examples:
#[OA\Property(type: 'integer', examples: [80, 443])]Items attribute's constructor docblock is repaired, so the reference page shows its declared parameter types instead of falling back to the native onesCases that used to pass in silence now warn: an @OA\Examples under a schema with no value, value and externalValue set together, and any other annotation passed as an example.
The cookbook has a new section on schema examples and how they differ from a media type's.
fix(Attributes): repair the Items constructor docblock by @DerManoMann in #2179
Stacked on #2178 and squash-merged, so it carries that change too — most of this release is in that commit.
Full Changelog: 6.8.0...6.8.1
fix(Annotations): compile a schema's examples as a list in classic too by @DerManoMann in #2176
Full Changelog: 6.7.2...6.8.0
chore(Deps): follow rector 2.6.5 rule changes and pin tooling by @DerManoMann in #2143
TypedList::clear() method and update tests by @DerManoMann in #2145phpstan/phpstan to an exact version by @DerManoMann in #2151$argv in the doc generator entry point by @DerManoMann in #2152Undefined::UNDEFINED for every mixed property by @DerManoMann in #2150Full Changelog: 6.7.1...6.7.2
Fix typo in documentation for spec attributes by @Jonezzyboy in #2133
Full Changelog: 6.7.0...6.7.1
docs(Spec): update docs about implicit Property shortcut in combination with OA\Items by @DerManoMann in #2127
OA\Items by @DerManoMann in #2127Contracts namespace for all spec related interfaces by @DerManoMann in #2129Full Changelog: 6.6.0...6.7.0
fix(Spec): update remaining $ref type-hints to accept OA\Schema\Ref by @DerManoMann in #2119
$ref type-hints to accept OA\Schema\Ref by @DerManoMann in #2119OA\Property when stacking witbh OA\Schema by @DerManoMann in #2121contains() method by @DerManoMann in #2122ref with eachRef() by @DerManoMann in #2123ComponentIndex for lazy resolution of $ref values and component lookups by @DerManoMann in #2124Full Changelog: 6.5.3...6.6.0
feat(Spec): refactor/assembler level resolution by @DerManoMann in #2104
Post, 'Put, PatchandDelete` by @DerManoMann in #2109getDirectInterfaces into AttributeFactory by @DerManoMann in #2112mergeAllOf and dedupAllOfRefs logic into Refs augmenter by @DerManoMann in #2113php-cs-fixer rule ordered_class_elements by @DerManoMann in #2116OA\Schema\Ref which can be used as schema and on ref: directyl by @DerManoMann in #2117Full Changelog: 6.5.2...6.5.3
feat(Spec): improve AttributeTranslatorInterface by @DerManoMann in #2096
AttributeTranslatorInterface by @DerManoMann in #2096TokenScanner by @DerManoMann in #2099foreach loops with unpacking operations by @DerManoMann in #2102description attribute from #[OA\Property] annotation by @DerManoMann in #2103OpenApi31Compiler: consolidate repetitive patterns by @DerManoMann in #2106Full Changelog: 6.5.1...6.5.2
feat(Spec): widen the type of the content parameter ( MediaType ) to accept MediaType|array|null by @DerManoMann in #2088
content parameter (MediaType) to accept MediaType|array|null by @DerManoMann in #2088Specefication to build Result by @DerManoMann in #2093Builder sources to accept \Reflector instances too (except in CLASSIC mode) by @DerManoMann in #2091Items by @DerManoMann in #2094spec-attributes.md; content no longer relevant or needed by @DerManoMann in #2095Full Changelog: 6.5.0...6.5.1
This version introduces the spec pipeline - a new set of attributes that will replace the current ( classic ) annotations/attributes. It is using a ra
This version introduces the spec pipeline - a new set of attributes that will replace the current (classic) annotations/attributes.
It is using a radically different approach in how the code is organized which makes it a lot easier to maintain.
Right now, the new code is disabled and nothing changes. Key is that in order to try the new code the new OpenApi\Builder needs to be used. Again - using the Builder will, by default, change nothing and the current classic code is used.
The new Builder::setMode() method allows to run the new spec pipeline in two modes:
HYBRID: Enables spec support where found and also routes all classic annotations/attributes through the new spec pipeline.Generator is used to load/instantiate classic annotations/attributes. Then a custom bridge translates all of those into new thin OpenApi\Spec attributes and adds them to the spec pipeline.SPEC: Only new spec attributes are processed.HYBRID is a good way to see how compatible things are and also a migration path, as a codebase could be upgrade file by file.
All spec pipeline related commits have been omitted from the change list for clarity and brevity. Going forward these will be marked as feat(Spec): and listed as all other changes.
All current tests pass in HYBRID mode. However, there hasn't been any testing on actual projects yet. Testing the new code in hybrid mode on existing projects and feedback would be greatly appreciated.
CustomName/Blink with CustomName-Blink in examples and annotations by @DerManoMann in #2069Full Changelog: 6.4.0...6.5.0
feat: add Builder as unified entry point by @DerManoMann in #2044
Full Changelog: 6.3.2...6.4.0
Remove dev dep composer/package-versions-deprecated by @DerManoMann in #2049
Utils namespace by @DerManoMann in #2041multiline_comment_opening_closing rule by @DerManoMann in #2043composer/package-versions-deprecated by @DerManoMann in #2049Full Changelog: 6.3.1...6.3.2
fix: add native array|string type to Serializer::doDeserializeBaseProperty() by @DerManoMann in #2036
Utils\DocBlockParser by @DerManoMann in #2038TokenScannerand other utils to OpenApi\Utils namespace by @DerManoMann in #2039Full Changelog: 6.3.0...6.3.1
fix: PHP 8.5 deprecation for null as array offset by @DerManoMann in #2032
Full Changelog: 6.2.0...6.3.0
Make AbstractAnnotation::$_context non-nullable by @krissss in #2008
AbstractAnnotation::$_context non-nullable by @krissss in #2008GeneratorAwareInterface::setGenerator() and align related code by @DerManoMann in #2013Full Changelog: 6.1.2...6.2.0
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →