zircote/swagger-php
Generate OpenAPI 3.0/3.1/3.2 docs from your PHP 8.2+ code using native attributes (preferred) or optional Doctrine annotations. Includes CLI and programmatic generation, parses phpdoc, provides helpful error reporting, and powers interactive API docs.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require zircote/swagger-php
For CLI usage globally:
composer global require zircote/swagger-php
Basic Attribute Usage: Add OpenAPI attributes to your controllers or DTOs. Example:
use OpenApi\Attributes as OAT;
#[OAT\Info(title: 'My API', version: '1.0')]
class MyController {
#[OAT\Get(path: '/users', summary: 'Get all users')]
public function index(): array { ... }
}
Generate Documentation:
$openapi = (new \OpenApi\Generator())->generate([__DIR__ . '/src']);
file_put_contents('api-docs.yaml', $openapi->toYaml());
./vendor/bin/openapi --output api-docs.yaml src/
Annotate your Laravel controllers with OpenAPI attributes to auto-generate interactive API docs. Use tools like Swagger UI to visualize the docs.
Annotate Laravel controllers for route-level documentation:
use OpenApi\Attributes as OAT;
#[OAT\Tag(name: 'Users')]
class UserController extends Controller {
#[OAT\Post(
path: '/users',
summary: 'Create a user',
requestBody: new OAT\RequestBody(
content: new \OpenApi\Attributes\JsonContent(ref: '#/components/schemas/UserCreate')
)
)]
public function store(Request $request) { ... }
}
Use attributes on DTOs for schema validation:
use OpenApi\Attributes as OAT;
#[OAT\Schema(description: 'User creation data')]
class UserCreate {
#[OAT\Property(type: 'string', example: 'john@example.com')]
public string $email;
#[OAT\Property(type: 'string', format: 'password')]
public string $password;
}
php artisan route:cache
./vendor/bin/openapi --output public/api-docs.yaml app/Http/Controllers/
openapi middleware to serve docs dynamically:
Route::get('/api-docs', function () {
return response((new \OpenApi\Generator())->generate([__DIR__ . '/app/Http/Controllers']));
});
Define reusable schemas in a central file (e.g., app/OpenApi/Components.php):
use OpenApi\Attributes as OAT;
#[OAT\Schema(schema: 'User')]
class User {
#[OAT\Property(type: 'string')] public string $id;
#[OAT\Property(type: 'string')] public string $name;
}
Specify OpenAPI version globally or per-generator:
$generator = new \OpenApi\Generator();
$generator->setVersion('3.1'); // Default is 3.0
nodemon to regenerate docs on file changes:
nodemon --watch app --exec "vendor/bin/openapi --output public/api-docs.yaml app/Http/Controllers"
# .github/workflows/docs.yml
jobs:
generate-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: composer install
- run: ./vendor/bin/openapi --output api-docs.yaml app/
- uses: actions/upload-artifact@v3
with:
name: api-docs
path: api-docs.yaml
Type Resolution:
TypeInfoTypeResolver is used (default in v6). Avoid LegacyTypeResolver (deprecated).@var list<User>) or attribute-based definitions:
#[OAT\Property(items: new OAT\Items(type: User::class))]
public array $users;
Annotation Conflicts:
@OAT\* attributes on the same method/class. Use #[OAT\Info] once per class.CLI Quirks:
./vendor/bin/openapi --output ./public/api-docs.yaml $(pwd)/app/Http/Controllers
--version flag may not persist in some setups. Use Generator::setVersion() programmatically if needed.Performance:
$generator->generate([__DIR__ . '/app/Http', __DIR__ . '/app/Models']);
storage/ directory and serve statically.OpenAPI 3.2:
unevaluatedProperties or query operations, but validate compatibility with your tools (e.g., Swagger UI may lag in support).Validation Errors:
$generator->setStrictValidation(true);
summary in #[OAT\Get]).Schema Mismatches:
#[OAT\Example] to provide concrete examples for complex schemas:
#[OAT\Property(type: 'object', example: ['id' => 1, 'name' => 'Test'])]
public array $data;
CLI Debugging:
--verbose for detailed output:
./vendor/bin/openapi --verbose --output api-docs.yaml app/
Custom Resolvers: Override type resolution for custom logic:
use OpenApi\Generator;
use OpenApi\TypeInfoTypeResolver;
$generator = new Generator();
$generator->setTypeResolver(new class extends TypeInfoTypeResolver {
protected function resolveCustomType(string $type): string {
return match ($type) {
'App\\Models\\User' => 'UserSchema',
default => parent::resolveType($type),
};
}
});
Post-Processing:
Modify the generated spec using OpenApi\OpenApi:
$openapi = $generator->generate([__DIR__ . '/app']);
$openapi->info->title = 'My Awesome API';
$openapi->servers = [new \OpenApi\Server(url: 'https://api.example.com/v1')];
Laravel Service Provider: Bind the generator to the container for reusable access:
// app/Providers/AppServiceProvider.php
public function register() {
$this->app->singleton(\OpenApi\Generator::class, function () {
return (new \OpenApi\Generator())
->setTypeResolver(new \OpenApi\TypeInfoTypeResolver())
->setVersion('3.1');
});
}
Route Model Binding: Document model binding in routes:
#[OAT\Get(
path: '/users/{id}',
summary: 'Get a user',
parameters: [new OAT\Parameter(
name: 'id',
in: 'path',
required: true,
schema: new OAT\Schema(type: 'integer')
)]
)]
public function show(User $user) { ... }
Form Requests:
Annotate FormRequest classes for validation docs:
use Illuminate\Foundation\Http\FormRequest;
use OpenApi\Attributes as OAT;
class StoreUserRequest extends FormRequest {
#[OAT\Property(type: 'string', example: 'john@example.com')]
public string $email;
}
API Resources:
Document API resources (e.g., Illuminate\Http\Resources\Json\JsonResource):
#[OAT\Schema(schema: 'UserResource')]
class UserResource extends JsonResource {
How can I help you explore Laravel packages today?