radebatz / openapi-introspector
Documents the routes your Laravel or Slim application already has: reads them from the framework and contributes them to the OpenAPI document swagger-php builds, so nothing is declared twice. The reverse of openapi-router.
Requires
- php: >=8.2
- psr/log: ^1.1 || ^2.0 || ^3.0
- zircote/swagger-php: ^6.17 || ^7.0
Requires (Dev)
- darkaonline/l5-swagger: ^11.2.1
- friendsofphp/php-cs-fixer: ^3.62.0
- laravel/framework: ^12.0 || ^13.0
- nyholm/psr7: ^1.8
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
- rector/rector: ^2.0
- slim/slim: ^4.0
Suggests
- darkaonline/l5-swagger: Serves the document in a Laravel application; Integrations\L5Swagger\BuilderFactory plugs the introspector into its builder_factory option (11.2.1 or later)
- radebatz/openapi-verifier: Verifies responses against the document, in Laravel and Slim tests, as PSR-15 middleware or in Guzzle and PSR-18 clients
Provides
None
Conflicts
None
Replaces
None
README
Introduction
Your framework already knows your API. Every route has a method, a path, path parameters with their constraints, and a handler whose docblock and signature describe it. Documenting that with swagger-php usually means writing it all a second time, as attributes, and then keeping the two in step by hand. The documentation drifts the first time a route changes and its attribute does not.
openapi-introspector reads the routes from the framework instead, and contributes them to the OpenAPI document swagger-php builds:
- Routes you have not documented still appear. Each gets its path parameters, with a route constraint as the parameter's pattern. Where the route dispatches to a class method, it also gets a summary, description and parameters from that method, exactly as if it had been scanned.
- Attributes you have written still win on description. They fold into the route's operation field by field, so you add them only for what a router cannot know: responses, request bodies, a better description.
- Documentation without a route is reported. An inventory lists every method and path, and says which are documented but not routed. That is a defect the document alone cannot show.
This is the reverse of openapi-router, which builds the routes from the attributes. The two are complements: openapi-router makes the document the source of the routes, the introspector makes the routes the source of the document, and a project can use both.
Spec attributes only. The package works inside swagger-php's spec pipeline, through
Builder::withSpecification(). Classic mode assembles no specification, so there is nothing
here for it to contribute to.
Supported frameworks:
Requirements
- PHP 8.2 or higher
zircote/swagger-php6.17 or higher, for thePathItemchain helpers,ComponentIndex::parameterKey()and themethods:split
Installation
composer require radebatz/openapi-introspector
Usage
The builder is set up as usual and configured as needed - then the introspector asked to add the framework routes:
use OpenApi\Builder; use OpenApi\Builder\Mode; use Radebatz\OpenApi\Introspector\Adapters\LaravelAdapter; use Radebatz\OpenApi\Introspector\Introspector; // create Builder as per usual $builder = (new Builder()) ->setMode(Mode::SPEC) ->addSource(__DIR__ . '/app'); // configure the introspector and add to the builder $introspector = (new Introspector()) ->addAdapter(new LaravelAdapter($router)); $introspector->register($builder);
The build itself is unchanged. The inventory is available once it has run:
$result = $builder->build(); $inventory = $introspector->inventory();
The adapter reads the framework's routes, and the introspector folds them into the document the
builder assembles. inventory() then says, for each method and path, whether attributes, a route
or both describe it.
As a swagger-php plugin
With swagger-php 7.3 or later, IntrospectorPlugin adds the same as register(), as a plugin.
It takes the adapters ready made:
use Radebatz\OpenApi\Introspector\IntrospectorPlugin; $builder->withPlugins([new IntrospectorPlugin(new LaravelAdapter($router))]);
Named by its class, the plugin takes settings instead: each key is an adapter class, and its value
that adapter's settings, [] for its defaults. Only class names and plain values, so a host that
passes plugin settings on from its configuration can carry the whole setup. LaravelAdapter reads
the routes from the running application when the document is built, those in the api middleware
group by default:
$builder->withPlugins([ IntrospectorPlugin::class => [LaravelAdapter::class => ['schemes' => ['sanctum' => 'bearerAuth']]], ]);
Laravel lists its settings. L5-Swagger has no option for plugins,
so there it takes a builder_factory of the project's own.
The plugin keeps the introspector to itself, so a build that needs the inventory uses
register(). Plugins are not used in classic mode.
Documentation
- Routes - where the routes come from, choosing them, and paths relative to a server
- Adapters - what a route contributes, and the adapters' options
- Laravel - model binding, form requests, authentication, other middleware, and the adapter from settings
- What wins - how routes and attributes fold together
- The inventory - which endpoints are documented, routed, or both
- Generating for L5-Swagger
- Verifying responses
License
The MIT License (MIT). See LICENSE.