swagger-php: Generating OpenAPI Documents from PHP Attributes
A php swagger annotation and parsing library
At a glance
- What is it?
- swagger-php turns PHP 8 attributes (or deprecated Doctrine annotations) into OpenAPI 3.0, 3.1 and 3.2 documents. It suits teams whose PHP code is already the source of truth for their API, and it asks for PHP 8.2 or newer.
- Who is it for?
- Adopt swagger-php if your API surface is written in PHP 8.2 or newer and you want the annotations to live beside the handlers, with a build step that emits YAML or JSON. Do not adopt it if your endpoints are defined in framework routing files, or if you are still on PHP 8.1, since the README states 8.2 is the floor.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What swagger-php solves, and for whom
A PHP REST API usually has two descriptions of itself: the routing and controller code that actually runs, and a hand-written OpenAPI file that drifts the moment someone renames a parameter. swagger-php attacks the second copy. It reads metadata attached to classes, methods and properties and emits an OpenAPI document, so the spec is regenerated from the code rather than maintained next to it.
The intended audience is a PHP team that already treats attributes as part of its source. The README's own example puts an #[OAT\Info] on a class and an #[OAT\Get] plus #[OAT\Response] on a method, which means the API description is written by the same people editing the handler. That is a good fit for a framework-agnostic codebase where routes are declared in PHP classes, and a poor fit for a project whose endpoints exist only as entries in a YAML routing file, because there is nothing for the scanner to attach to.
How the scanner, resolver and builder fit together
The pipeline has three visible stages. A source path is scanned for PHP elements carrying OpenAPI attributes or annotations. Those elements are then resolved into schemas, and the resolved set is compiled into a document for a chosen OpenAPI version. The README shows the entry point as a Builder object with addSource() and build(), returning a result that can be serialized with toYaml().
Type handling changed in version 6. The README states that resolution now goes through the TypeInfoTypeResolver class, which uses the symfony/type-info library, and that this supports native type hints plus complex generic hints written in phpdoc. The documented consequence is that an explicit #[OAT\Property] with a oneOf list of two schemas is equivalent to a plain @var list<SchemaOne|SchemaTwo> docblock. For a codebase that already documents array shapes in phpdoc, that removes a lot of attribute noise. The old behaviour is still reachable through LegacyTypeResolver, which the README marks as deprecated and scheduled for removal, so it is a migration aid rather than a long-term setting.
Version selection is explicit. Classic mode defaults to 3.0.0, while spec and hybrid modes default to 3.1.0. The CLI flag --version and the programmatic Builder::setVersion() change it. A team that has already published a 3.0 document should not assume an upgrade keeps the same output version.
Installing swagger-php and generating a first document
Installation is a single Composer command. The package requires PHP 8.2 or newer, and the README notes that doctrine/annotations became optional at version 4.8, so it is not pulled in unless you ask for it.
composer require zircote/swagger-phpIf you want the openapi executable available outside the project, the README suggests a global install plus adding Composer's global bin directory to your PATH.
composer global require zircote/swagger-phpFor a first real use, annotate one class and one method, then generate from the command line. The attribute names below are the ones the README uses in its own example.
use OpenApi\Attributes as OAT;
#[OAT\Info(title: 'My First API', version: '0.1')]
class MyApi
{
#[OAT\Get(path: '/api/resource.json')]
#[OAT\Response(response: '200', description: 'An example resource')]
public function getResource()
{
}
}Running the CLI against the directory that holds that file writes a static document. The README points at ./vendor/bin/openapi --help for the available options.
./vendor/bin/openapi src/The same thing can be done inside an application, which is the pattern the README gives for documentation that stays current. The result object is serialized and sent with a content type of application/x-yaml.
<?php
require('vendor/autoload.php');
$result = (new \OpenApi\Builder())
->addSource(['/path/to/project'])
->build();
header('Content-Type: application/x-yaml');
echo $result->toYaml();If your code still uses Doctrine annotations rather than attributes, add the library explicitly.
composer require doctrine/annotationsAnnotations are on the way out, and the beta pipeline is not finished
Two things in the README should shape an adoption decision. The first is the status of Doctrine annotations: they are supported, but the README states plainly that they are deprecated and may be removed in a future release. A codebase built on annotations is therefore on a path that ends. Converting to attributes is the documented direction, and the two styles are close enough in naming that the work is mechanical rather than architectural, but it is work, and it touches every annotated class.
The second is the spec attributes pipeline, described as beta and available since 6.5.0. It introduces typed attributes under the OpenApi\Spec namespace and a third processing mode. Hybrid mode is the interesting part for existing users: the README says it runs the classic scanner but swaps in the new augmenter pipeline and version-aware compilers, and that existing OpenApi\Attributes code needs no changes. That makes hybrid a low-commitment way to exercise the new code path, but it is still labelled beta, so a team that needs predictable output from a release build has a reason to stay on classic mode until the label changes.
A third limitation is structural rather than stated. The scanner reads PHP source, so anything that describes your API but lives outside PHP (a gateway config, a hand-written schema fragment) is not part of the generated document unless you wire it in yourself. The README does not document a merge or overlay mechanism for that case.
swagger-php compared with a spec-first workflow
The real alternative is not another PHP library but a different direction of travel: writing the OpenAPI document by hand or in a design tool first, then generating server stubs or validating responses against it. In that workflow the document is the contract and the code follows it; in swagger-php the code is the contract and the document follows it.
The difference shows up in review. With a spec-first file, a change to a response shape appears as a diff in a YAML file that reviewers can read without opening PHP. With swagger-php, the same change appears as an edit to an attribute or a phpdoc block inside a class, and the YAML is a build artifact. That is better for keeping the two in sync and worse for anyone outside the PHP team who wants to read or approve the contract. It also means the generated file should not be hand-edited, since the next build overwrites it.
A second practical difference is tooling. The repository carries a package.json named swagger-php-tools with a single script, redocly, and a devDependency on @redocly/cli, plus redocly.yaml and .redocly.lint-ignore.yaml at the top level. That is a linting setup for the generated documents rather than part of the library, which is a reasonable signal that the maintainers expect output to be checked by an external OpenAPI linter before it ships.
Licence, releases and what upgrades cost
The package is Apache-2.0, and the repository carries both a LICENSE and a NOTICE file. Apache-2.0 permits commercial and closed-source use and includes a patent grant; the NOTICE file is the part to be aware of, because redistribution typically requires carrying its contents. That is a description of the licence text, not legal advice, and anyone embedding the library in a distributed product should read the NOTICE themselves.
On maintenance, the last push to master was on 2026-09-22, and releases in the same window include 6.10.0 on 2026-09-22, 6.9.0 on 2026-09-13 and 6.8.1 on 2026-09-09. The cadence is frequent and the repository is not archived.
The upgrade cost is concentrated in two places. Moving from version 5 to version 6 changes type resolution, and the README offers LegacyTypeResolver as a deprecated bridge, so a large codebase can migrate in stages. Moving off Doctrine annotations is a separate, unavoidable edit if you are still using them. Neither migration changes your HTTP layer, which keeps the blast radius inside the documentation code. The README does not document a rollback path for generated output, so treat the emitted YAML or JSON as reproducible from a known commit rather than as something to revert in place.
Editorial conclusion
Adopt swagger-php if your API surface is written in PHP 8.2 or newer and you want the annotations to live beside the handlers, with a build step that emits YAML or JSON. Do not adopt it if your endpoints are defined in framework routing files, or if you are still on PHP 8.1, since the README states 8.2 is the floor. Before committing, verify that your framework's routes are actually picked up by a first scan of src/, and check whether you need doctrine/annotations installed separately, because it is no longer a default dependency.
Frequently asked questions
Is Swagger outdated?
The name Swagger now refers to the tooling around the OpenAPI specification, and swagger-php itself generates OpenAPI 3.0, 3.1 and 3.2 documents. The repository is not archived and the last push to master was on 2026-09-22, with release 6.10.0 published the same day.
Is Swagger now called OpenAPI?
The README describes swagger-php as generating interactive OpenAPI documentation and links to openapis.org for the specification. The package name and the OpenApi\Attributes namespace keep the swagger-php name while the emitted documents are OpenAPI 3.0, 3.1 or 3.2.
Is Swagger UI like Postman?
The README does not cover Swagger UI or Postman, so nothing in the repository material describes that comparison. What swagger-php produces is the OpenAPI document itself, which a UI tool would then render.
What is replacing Swagger?
Within this project the direction is stated in the README: Doctrine annotations are deprecated and may be removed, while PHP attributes are preferred and a beta spec attributes pipeline using the OpenApi\Spec namespace has been available since 6.5.0.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/zircote-swagger-php)