zircote/swagger-php
Generate interactive OpenAPI 3.0, 3.1, and 3.2 documentation for RESTful PHP APIs using PHP attributes (preferred) or doctrine annotations. Use programmatically or via CLI, with helpful error reporting and hints. Requires PHP 8.2+.
Tag on a method or class tags its operations#[OA\Tag] can now go on a method as well as a class. On a method it also tags that method's operations, and on a class every operation of the class. A class with no operations, such as one holding only Info, still only declares the tag. (#2274)
#[OA\Tag(name: 'users', description: 'Everything about users')]
class UserController
{
#[OA\Tag(name: 'admin')]
#[OA\Operation\Delete(path: '/users/{id}')]
public function delete(int $id) {}
}
DELETE /users/{id} is tagged admin and users. The same tag declared in several places now folds into one, so a name-only tag no longer replaces a description declared elsewhere. Only a field set differently is reported.
Result::log(), warnings() and errors() are deprecated and removed in 8.0. In spec and hybrid mode they never held the whole build log: a class skipped during the scan was reported to the logger only. The logger set with Builder::setLogger() receives every message. isValid() is unchanged. (#2266)
new OA\Operation() without a method no longer throws. An operation that reaches the compiler still without one is left out of the document, now with a warning. (#2271)ReflectionClass source is read for classic attributes too. Before, only the spec half saw it. A method or property reflector passed as a source is reported rather than dropped. (#2272)eval(), is read through reflection in spec, and skipped with a warning by the classic analyser. Before, a hybrid build failed. (#2273)#[OA\Components] to a class whose members declare keyed components (#2263).Tag migrated by the set is now a valid attribute. Before, the class was skipped. (#2274)This changes spec output. A Tag on a class that also declares operations now tags those operations. Move the tag to a class without operations to keep it a declaration only. (#2274)
#[OA\Components]This changes spec output. A keyed component declared on a method or property is a component only when its class carries #[OA\Components]. Before, one on a method was kept and one on a property was lost without a warning. Outside a holder it is now left out with a warning that says to add the wrapper. A schema on a property of a #[OA\Components] class becomes a component named after the property. (#2263)
This changes hybrid and spec output. A schema with nullable: true, no type and a oneOf or anyOf lost the null in 3.1 and 3.2. It now gets a {type: null} branch, as in 3.0 and classic. (#2267)
summary and description beside a $refThis changes hybrid and spec output. 3.1 and 3.2 now keep summary and description on a Reference Object, as classic does. 3.0 does not allow them, so they are still dropped there, now with a warning. A security scheme that is a $ref compiles to a reference instead of an empty object. (#2268)
type: object inferred from allOfThis changes hybrid and spec output. An untyped schema with an allOf got type: object, which made a composition of non-objects unsatisfiable. The type is now left off, as in classic. (#2269)
This changes hybrid and spec output. An operation is written only under a method its version defines. A query operation is left out of 3.0 and 3.1 with a warning, as classic leaves it out. 3.2 keeps a QUERY request body, which it dropped before. (#2270)
This changes hybrid output. A named classic Schema on a property is a component, as in classic. Hybrid left it out, and every $ref to it pointed at nothing. (#2264)
This changes hybrid output. A classic tag declared with a description and again by name only keeps the description. Classic keeps the last declaration whole. (#2274)
A serialized Context leaves out the non-OpenApi attributes found next to an annotation. One holding a closure, such as Symfony's #[IsGranted] on PHP 8.5, made serialize() fail, so a cached OpenApi could not be written. After unserialize() those attributes are gone. (#2265, nelmio/NelmioApiDocBundle#2817)
Full Changelog: https://github.com/zircote/swagger-php/compare/6.15.0...6.16.0
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: https://github.com/zircote/swagger-php/compare/6.11.0...6.12.0
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](https://github.com/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: https://github.com/zircote/swagger-php/compare/6.10.0...6.11.0
OA\Items by @DerManoMann in https://github.com/zircote/swagger-php/pull/2127Contracts namespace for all spec related interfaces by @DerManoMann in https://github.com/zircote/swagger-php/pull/2129Full Changelog: https://github.com/zircote/swagger-php/compare/6.6.0...6.7.0
$ref type-hints to accept OA\Schema\Ref by @DerManoMann in https://github.com/zircote/swagger-php/pull/2119OA\Property when stacking witbh OA\Schema by @DerManoMann in https://github.com/zircote/swagger-php/pull/2121contains() method by @DerManoMann in https://github.com/zircote/swagger-php/pull/2122ref with eachRef() by @DerManoMann in https://github.com/zircote/swagger-php/pull/2123ComponentIndex for lazy resolution of $ref values and component lookups by @DerManoMann in https://github.com/zircote/swagger-php/pull/2124Full Changelog: https://github.com/zircote/swagger-php/compare/6.5.3...6.6.0
Post, 'Put, PatchandDelete` by @DerManoMann in https://github.com/zircote/swagger-php/pull/2109getDirectInterfaces into AttributeFactory by @DerManoMann in https://github.com/zircote/swagger-php/pull/2112mergeAllOf and dedupAllOfRefs logic into Refs augmenter by @DerManoMann in https://github.com/zircote/swagger-php/pull/2113php-cs-fixer rule ordered_class_elements by @DerManoMann in https://github.com/zircote/swagger-php/pull/2116OA\Schema\Ref which can be used as schema and on ref: directyl by @DerManoMann in https://github.com/zircote/swagger-php/pull/2117Full Changelog: https://github.com/zircote/swagger-php/compare/6.5.2...6.5.3
AttributeTranslatorInterface by @DerManoMann in https://github.com/zircote/swagger-php/pull/2096TokenScanner by @DerManoMann in https://github.com/zircote/swagger-php/pull/2099foreach loops with unpacking operations by @DerManoMann in https://github.com/zircote/swagger-php/pull/2102description attribute from #[OA\Property] annotation by @DerManoMann in https://github.com/zircote/swagger-php/pull/2103OpenApi31Compiler: consolidate repetitive patterns by @DerManoMann in https://github.com/zircote/swagger-php/pull/2106Full Changelog: https://github.com/zircote/swagger-php/compare/6.5.1...6.5.2
content parameter (MediaType) to accept MediaType|array|null by @DerManoMann in https://github.com/zircote/swagger-php/pull/2088Specefication to build Result by @DerManoMann in https://github.com/zircote/swagger-php/pull/2093Builder sources to accept \Reflector instances too (except in CLASSIC mode) by @DerManoMann in https://github.com/zircote/swagger-php/pull/2091Items by @DerManoMann in https://github.com/zircote/swagger-php/pull/2094spec-attributes.md; content no longer relevant or needed by @DerManoMann in https://github.com/zircote/swagger-php/pull/2095Full Changelog: https://github.com/zircote/swagger-php/compare/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 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.
Under the hood the classic 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 https://github.com/zircote/swagger-php/pull/2069Full Changelog: https://github.com/zircote/swagger-php/compare/6.4.0...6.5.0
Full Changelog: https://github.com/zircote/swagger-php/compare/6.3.2...6.4.0
Utils namespace by @DerManoMann in https://github.com/zircote/swagger-php/pull/2041multiline_comment_opening_closing rule by @DerManoMann in https://github.com/zircote/swagger-php/pull/2043composer/package-versions-deprecated by @DerManoMann in https://github.com/zircote/swagger-php/pull/2049Full Changelog: https://github.com/zircote/swagger-php/compare/6.3.1...6.3.2
Utils\DocBlockParser by @DerManoMann in https://github.com/zircote/swagger-php/pull/2038TokenScannerand other utils to OpenApi\Utils namespace by @DerManoMann in https://github.com/zircote/swagger-php/pull/2039Full Changelog: https://github.com/zircote/swagger-php/compare/6.3.0...6.3.1
Full Changelog: https://github.com/zircote/swagger-php/compare/6.2.0...6.3.0
AbstractAnnotation::$_context non-nullable by @krissss in https://github.com/zircote/swagger-php/pull/2008GeneratorAwareInterface::setGenerator() and align related code by @DerManoMann in https://github.com/zircote/swagger-php/pull/2013Full Changelog: https://github.com/zircote/swagger-php/compare/6.1.2...6.2.0
array<K, V> to type: object + additionalProperties by @krissss in https://github.com/zircote/swagger-php/pull/2003Full Changelog: https://github.com/zircote/swagger-php/compare/6.1.1...6.1.2
Full Changelog: https://github.com/zircote/swagger-php/compare/6.1.0...6.1.1
ext-json by @DerManoMann in https://github.com/zircote/swagger-php/pull/1974bin/openapi to Symfony Console by @yceruto in https://github.com/zircote/swagger-php/pull/1977Full Changelog: https://github.com/zircote/swagger-php/compare/6.0.6...6.1.0
Full Changelog: https://github.com/zircote/swagger-php/compare/6.0.5...6.0.6
Full Changelog: https://github.com/zircote/swagger-php/compare/5.8.2...5.8.3
npm tools deps by @github-actions[bot] in https://github.com/zircote/swagger-php/pull/1955deepObject parameter to attribute Parameter by @github-actions[bot] in https://github.com/zircote/swagger-php/pull/1958Full Changelog: https://github.com/zircote/swagger-php/compare/5.8.1...5.8.2
npm tools deps by @DerManoMann in https://github.com/zircote/swagger-php/pull/1954deepObject parameter to attribute Parameter by @DerManoMann in https://github.com/zircote/swagger-php/pull/1957Full Changelog: https://github.com/zircote/swagger-php/compare/6.0.4...6.0.5
master by @DerManoMann in https://github.com/zircote/swagger-php/pull/1938Generator::isDefault() where appropriate by @DerManoMann in https://github.com/zircote/swagger-php/pull/1940TypeInfoTypeResolver by @DerManoMann in https://github.com/zircote/swagger-php/pull/1945Full Changelog: https://github.com/zircote/swagger-php/compare/6.0.3...6.0.4
Generator::isDefault() where appropriate by @github-actions[bot] in https://github.com/zircote/swagger-php/pull/1941Full Changelog: https://github.com/zircote/swagger-php/compare/5.8.0...5.8.1
unevaluatedProperties support by @github-actions[bot] in https://github.com/zircote/swagger-php/pull/1908Items' case to NestedSchema scratch fixture by @DerManoMann in https://github.com/zircote/swagger-php/pull/1933Full Changelog: https://github.com/zircote/swagger-php/compare/5.7.8...5.8.0
Generator default config and allow to show in CLI by @DerManoMann in https://github.com/zircote/swagger-php/pull/1925Full Changelog: https://github.com/zircote/swagger-php/compare/6.0.2...6.0.3
Schema with items set (#1916) by @DerManoMann in https://github.com/zircote/swagger-php/pull/1916Full Changelog: https://github.com/zircote/swagger-php/compare/6.0.1...6.0.2
phpunit deps and some related issues by @DerManoMann in https://github.com/zircote/swagger-php/pull/1909rector/rector dependency to ^2.3.1 by @DerManoMann in https://github.com/zircote/swagger-php/pull/1913Full Changelog: https://github.com/zircote/swagger-php/compare/6.0.0...6.0.1
Tag annotations by @github-actions[bot] in https://github.com/zircote/swagger-php/pull/1897query operation by @github-actions[bot] in https://github.com/zircote/swagger-php/pull/1898rector/rector dependency to ^2.3.1 by @github-actions[bot] in https://github.com/zircote/swagger-php/pull/1914rector/rector dependency to ^2.3.1" by @DerManoMann in https://github.com/zircote/swagger-php/pull/1915Full Changelog: https://github.com/zircote/swagger-php/compare/5.7.7...5.7.8
radebatz/type-info-extras is now a required dependencyTypeInfoTypeResolver is now the default^8.0 to deps list by @DerManoMann in https://github.com/zircote/swagger-php/pull/1854method_exists checks by @DerManoMann in https://github.com/zircote/swagger-php/pull/1862doctrine/annotations by @DerManoMann in https://github.com/zircote/swagger-php/pull/1870'3.1.2' and '3.2.0' by @DerManoMann in https://github.com/zircote/swagger-php/pull/1871patternProperties to attributes by @DerManoMann in https://github.com/zircote/swagger-php/pull/1876$additionalProperties by @DerManoMann in https://github.com/zircote/swagger-php/pull/1878jsonSerialize() return type by @DerManoMann in https://github.com/zircote/swagger-php/pull/1883summary/description default value to support null as user provided value by @DerManoMann in https://github.com/zircote/swagger-php/pull/1886Tag annotations by @DerManoMann in https://github.com/zircote/swagger-php/pull/1893query operation by @DerManoMann in https://github.com/zircote/swagger-php/pull/1894unevaluatedProperties support by @DerManoMann in https://github.com/zircote/swagger-php/pull/1907Full Changelog: https://github.com/zircote/swagger-php/compare/5.7.3...6.0.0
Tag annotations by @DerManoMann in https://github.com/zircote/swagger-php/pull/1893query operation by @DerManoMann in https://github.com/zircote/swagger-php/pull/1894Full Changelog: https://github.com/zircote/swagger-php/compare/6.0.0-RC3...6.0.0-RC4
doctrine/annotations by @DerManoMann in https://github.com/zircote/swagger-php/pull/1870'3.1.2' and '3.2.0' by @DerManoMann in https://github.com/zircote/swagger-php/pull/1871patternProperties to attributes by @DerManoMann in https://github.com/zircote/swagger-php/pull/1876$additionalProperties by @DerManoMann in https://github.com/zircote/swagger-php/pull/1878jsonSerialize() return type by @DerManoMann in https://github.com/zircote/swagger-php/pull/1883summary/description default value to support null as user provided value by @DerManoMann in https://github.com/zircote/swagger-php/pull/1886Full Changelog: https://github.com/zircote/swagger-php/compare/6.0.0-RC2...6.0.0-RC3
'3.1.2' and '3.2.0' by @DerManoMann in https://github.com/zircote/swagger-php/pull/1872patternProperties to attributes by @DerManoMann in https://github.com/zircote/swagger-php/pull/1877summary/description default value to support null as user provided value (#1886) by @DerManoMann in https://github.com/zircote/swagger-php/pull/1889Full Changelog: https://github.com/zircote/swagger-php/compare/5.7.6...5.7.7
Full Changelog: https://github.com/zircote/swagger-php/compare/5.7.5...5.7.6
^8.0 to deps list by @DerManoMann in https://github.com/zircote/swagger-php/pull/1854method_exists checks by @DerManoMann in https://github.com/zircote/swagger-php/pull/1862Full Changelog: https://github.com/zircote/swagger-php/compare/6.0.0-RC1...6.0.0-RC2
AugmentItems processor by @DerManoMann in https://github.com/zircote/swagger-php/pull/1863Full Changelog: https://github.com/zircote/swagger-php/compare/5.7.4...5.7.5
Full Changelog: https://github.com/zircote/swagger-php/compare/5.7.3...5.7.4
Full Changelog: https://github.com/zircote/swagger-php/compare/5.7.3...6.0.0-RC1
oneOf, allOf and anyOf when deciding on whether to modify the schema type by @DerManoMann in https://github.com/zircote/swagger-php/pull/1847Full Changelog: https://github.com/zircote/swagger-php/compare/5.7.2...5.7.3
array in LegacyTypeResolver by @DerManoMann in https://github.com/zircote/swagger-php/pull/1844false by @DerManoMann in https://github.com/zircote/swagger-php/pull/1849Full Changelog: https://github.com/zircote/swagger-php/compare/5.7.1...5.7.2
Components into a single one by @DerManoMann in https://github.com/zircote/swagger-php/pull/1836LegacyTypeResolver by @DerManoMann in https://github.com/zircote/swagger-php/pull/1843MediaType::encoding values by @DerManoMann in https://github.com/zircote/swagger-php/pull/1845Full Changelog: https://github.com/zircote/swagger-php/compare/5.7.0...5.7.1
Encoding annotation to manage MediaType encodings by @DerManoMann in https://github.com/zircote/swagger-php/pull/1835Full Changelog: https://github.com/zircote/swagger-php/compare/5.6.1...5.7.0
contentEncoding and contentMediaType to Schema by @DerManoMann in https://github.com/zircote/swagger-php/pull/1831method_exists on reflector by @DjordyKoert in https://github.com/zircote/swagger-php/pull/1833Full Changelog: https://github.com/zircote/swagger-php/compare/5.6.0...5.6.1
Type resolver refactor by @DerManoMann in https://github.com/zircote/swagger-php/pull/1829 This 2nd round moved all data manipulation from the analyser and annotation factories into the a appropriate processors and improved type resolvers. Drops support for:
Context::typeContext:nullableMark Analysis::getSchemaForSource() as deprecated by @DerManoMann in https://github.com/zircote/swagger-php/pull/1830
Full Changelog: https://github.com/zircote/swagger-php/compare/5.5.2...5.6.0
Full Changelog: https://github.com/zircote/swagger-php/compare/5.5.1...5.5.2
Context has a reflector and is not nested by @DerManoMann in https://github.com/zircote/swagger-php/pull/1824Full Changelog: https://github.com/zircote/swagger-php/compare/5.5.0...5.5.1
radebatz/type-info-extras by @DerManoMann in https://github.com/zircote/swagger-php/pull/1821Full Changelog: https://github.com/zircote/swagger-php/compare/5.4.2...5.5.0
array as $type type in mapNativeType() by @DerManoMann in https://github.com/zircote/swagger-php/pull/1812Full Changelog: https://github.com/zircote/swagger-php/compare/5.4.1...5.4.2
GeneratorAwareInterface by @DerManoMann in https://github.com/zircote/swagger-php/pull/1813GeneratorAwareInterface also when setting new Pipeline by @DerManoMann in https://github.com/zircote/swagger-php/pull/1814Analysis::process() as deprecated by @DerManoMann in https://github.com/zircote/swagger-php/pull/1815Full Changelog: https://github.com/zircote/swagger-php/compare/5.4.0...5.4.1
How can I help you explore Laravel packages today?