Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Swagger-PHP
https://github.com/zircote/swagger-php
Admin
A php swagger annotation and parsing library
Tokens:
75,803
Snippets:
661
Trust Score:
9.3
Update:
1 week ago
Context
Skills
Chat
Benchmark
89.6
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# swagger-php swagger-php is a PHP library that generates interactive OpenAPI documentation for RESTful APIs using PHP 8+ attributes (preferred) or doctrine annotations. It scans your PHP codebase, extracts API metadata from attributes/annotations attached to classes, methods, and properties, then produces an OpenAPI 3.0, 3.1, or 3.2 specification document in YAML or JSON format. The library supports automatic type resolution from PHP type hints and PHPDoc comments, making schema definition simpler. The library provides both a command-line interface for generating static documentation files and a programmatic PHP API through the `Generator` class for dynamic, on-the-fly documentation generation. It includes a comprehensive set of attributes that map directly to OpenAPI specification elements, along with built-in processors that augment, merge, and validate annotations during the generation process. ## Generator Class - Basic Usage The `Generator` class is the main entry point for programmatically generating OpenAPI specifications. It scans source files, processes annotations, and outputs the complete specification. ```php <?php require('vendor/autoload.php'); // Basic usage - scan a directory and output YAML $openapi = (new \OpenApi\Generator())->generate(['/path/to/your/api']); // Output as YAML header('Content-Type: application/x-yaml'); echo $openapi->toYaml(); // Or output as JSON header('Content-Type: application/json'); echo $openapi->toJson(); ``` ## Generator Class - Advanced Configuration The Generator supports extensive customization including custom processors, version selection, file filtering, and custom analyzers for complex projects. ```php <?php require('vendor/autoload.php'); $validate = true; $logger = new \Psr\Log\NullLogger(); // Use Symfony Finder for file discovery $finder = \Symfony\Component\Finder\Finder::create() ->files() ->name('*.php') ->in(__DIR__); $context = new \OpenApi\Context(); $openapi = (new \OpenApi\Generator($logger)) ->setProcessorPipeline(new \OpenApi\Pipeline([/* custom processors */])) ->setAnalyser(new \OpenApi\Analysers\ReflectionAnalyser([ new \OpenApi\Analysers\DocBlockAnnotationFactory(), new \OpenApi\Analysers\AttributeAnnotationFactory() ])) ->setVersion(\OpenApi\Annotations\OpenApi::VERSION_3_1_0) ->generate(['/path/to/project', $finder], new \OpenApi\Analysis([], $context), $validate); // Using SourceFinder with exclude patterns $exclude = ['tests', 'vendor']; $pattern = '*.php'; $openapi = (new \OpenApi\Generator()) ->generate(new \OpenApi\SourceFinder(__DIR__, $exclude, $pattern)); ``` ## Command Line Interface The `openapi` CLI tool generates OpenAPI documentation to static YAML or JSON files. It supports various options for customization including exclusions, bootstrap files, and processor configuration. ```bash # Basic usage - scan 'app' directory, output to openapi.yaml ./vendor/bin/openapi app -o openapi.yaml # Output as JSON (format detected from extension) ./vendor/bin/openapi app -o openapi.json # Force specific format ./vendor/bin/openapi app -o api-spec --format yaml # Exclude directories ./vendor/bin/openapi app -o openapi.yaml --exclude vendor,tests # Use specific OpenAPI version ./vendor/bin/openapi app -o openapi.yaml --version 3.1.0 # Configure processors via command line ./vendor/bin/openapi app -o openapi.yaml -c operationId.hash=false # Bootstrap file for constants/autoloading ./vendor/bin/openapi -b config/constants.php app -o openapi.yaml # Filter by file pattern ./vendor/bin/openapi app --pattern "*.php" -o openapi.yaml # Show all options ./vendor/bin/openapi --help ``` ## Info Attribute - API Metadata The `#[OA\Info]` attribute defines top-level API metadata including title, version, description, contact information, and license. This is required for a valid OpenAPI specification. ```php <?php use OpenApi\Attributes as OA; #[OA\OpenApi(openapi: OA\OpenApi::VERSION_3_1_0)] #[OA\Info( version: '1.0.0', title: 'My API', description: 'A comprehensive API for managing resources' )] #[OA\Contact( name: 'API Support', email: 'support@example.com', url: 'https://example.com/support' )] #[OA\License(name: 'MIT', identifier: 'MIT')] #[OA\Server(url: 'https://api.example.com/v1', description: 'Production server')] #[OA\Server(url: 'https://staging-api.example.com/v1', description: 'Staging server')] class OpenApiSpec { } ``` ## Schema Attribute - Model Definition The `#[OA\Schema]` attribute defines data models/schemas. It can be applied to classes, and properties are automatically discovered from class properties with `#[OA\Property]` attributes. ```php <?php use OpenApi\Attributes as OA; #[OA\Schema( title: 'Pet', description: 'Pet model', required: ['name', 'photoUrls'] )] class Pet { #[OA\Property(description: 'Pet ID', format: 'int64', example: 1)] public int $id; #[OA\Property(description: 'Pet name', example: 'Buddy')] public string $name; #[OA\Property( description: 'Photo URLs', type: 'array', items: new OA\Items(type: 'string', example: 'https://example.com/photo.jpg') )] public array $photoUrls; #[OA\Property(description: 'Pet status', enum: ['available', 'pending', 'sold'])] public string $status; // Type inference from PHP type hints works automatically #[OA\Property] public ?Category $category; } #[OA\Schema] class Category { #[OA\Property(format: 'int64')] public int $id; #[OA\Property] public string $name; } ``` ## HTTP Method Attributes - GET, POST, PUT, DELETE, PATCH HTTP method attributes define API operations. They support path parameters, query parameters, request bodies, responses, security, and tags. ```php <?php use OpenApi\Attributes as OA; class PetController { #[OA\Get( path: '/pets/{petId}', operationId: 'getPetById', summary: 'Find pet by ID', description: 'Returns a single pet', tags: ['pet'] )] #[OA\Parameter( name: 'petId', description: 'ID of pet to return', in: 'path', required: true, schema: new OA\Schema(type: 'integer', format: 'int64') )] #[OA\Response( response: 200, description: 'Successful operation', content: new OA\JsonContent(ref: Pet::class) )] #[OA\Response(response: 404, description: 'Pet not found')] public function getPetById(int $petId) { } #[OA\Post( path: '/pets', operationId: 'addPet', summary: 'Add a new pet', tags: ['pet'] )] #[OA\RequestBody( description: 'Pet object to be added', required: true, content: new OA\JsonContent(ref: Pet::class) )] #[OA\Response( response: 201, description: 'Pet created', content: new OA\JsonContent(ref: Pet::class) )] #[OA\Response(response: 405, description: 'Invalid input')] public function addPet() { } #[OA\Put(path: '/pets/{petId}', operationId: 'updatePet', tags: ['pet'])] #[OA\Parameter(name: 'petId', in: 'path', required: true, schema: new OA\Schema(type: 'integer'))] #[OA\RequestBody(required: true, content: new OA\JsonContent(ref: Pet::class))] #[OA\Response(response: 200, description: 'Pet updated')] #[OA\Response(response: 400, description: 'Invalid ID supplied')] #[OA\Response(response: 404, description: 'Pet not found')] public function updatePet(int $petId) { } #[OA\Delete(path: '/pets/{petId}', operationId: 'deletePet', tags: ['pet'])] #[OA\Parameter(name: 'petId', in: 'path', required: true, schema: new OA\Schema(type: 'integer'))] #[OA\Response(response: 204, description: 'Pet deleted')] #[OA\Response(response: 404, description: 'Pet not found')] public function deletePet(int $petId) { } } ``` ## Parameter Attributes - Path, Query, Header, Cookie Parameters can be defined for different locations (path, query, header, cookie) with various options including schema types, validation rules, and examples. ```php <?php use OpenApi\Attributes as OA; class SearchController { #[OA\Get(path: '/search', operationId: 'search', tags: ['search'])] #[OA\Parameter( name: 'q', description: 'Search query', in: 'query', required: true, schema: new OA\Schema(type: 'string', minLength: 1, maxLength: 100) )] #[OA\Parameter( name: 'page', description: 'Page number', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1, minimum: 1) )] #[OA\Parameter( name: 'limit', description: 'Results per page', in: 'query', schema: new OA\Schema(type: 'integer', default: 20, minimum: 1, maximum: 100) )] #[OA\Parameter( name: 'sort', description: 'Sort order', in: 'query', schema: new OA\Schema(type: 'string', enum: ['asc', 'desc'], default: 'desc') )] #[OA\HeaderParameter( name: 'X-Request-ID', description: 'Unique request identifier', schema: new OA\Schema(type: 'string', format: 'uuid') )] #[OA\Response(response: 200, description: 'Search results')] public function search() { } // Using PathParameter shortcut #[OA\Get(path: '/users/{userId}/posts/{postId}', tags: ['posts'])] public function getPost( #[OA\PathParameter(description: 'User ID')] int $userId, #[OA\PathParameter(description: 'Post ID')] int $postId ) { } // Multi-value query parameter (arrays) #[OA\Get(path: '/filter', tags: ['filter'])] #[OA\Parameter( name: 'tags[]', description: 'Tags to filter by', in: 'query', required: false, schema: new OA\Schema(type: 'array', items: new OA\Items(type: 'string')), explode: true )] #[OA\Response(response: 200, description: 'Filtered results')] public function filter() { } } ``` ## Response Attribute - Defining API Responses Responses define the possible outcomes of an operation including status codes, descriptions, headers, and content schemas. ```php <?php use OpenApi\Attributes as OA; class UserController { #[OA\Get(path: '/users', operationId: 'listUsers', tags: ['users'])] #[OA\Response( response: 200, description: 'Successful operation', headers: [ new OA\Header( header: 'X-Rate-Limit', description: 'Calls per hour allowed', schema: new OA\Schema(type: 'integer') ), new OA\Header( header: 'X-Rate-Limit-Remaining', description: 'Remaining calls', schema: new OA\Schema(type: 'integer') ) ], content: new OA\JsonContent( type: 'object', properties: [ new OA\Property(property: 'data', type: 'array', items: new OA\Items(ref: User::class)), new OA\Property(property: 'total', type: 'integer', example: 100), new OA\Property(property: 'page', type: 'integer', example: 1) ] ) )] #[OA\Response( response: 401, description: 'Unauthorized', content: new OA\JsonContent( properties: [ new OA\Property(property: 'error', type: 'string', example: 'Invalid credentials') ] ) )] #[OA\Response(response: 500, description: 'Internal server error')] public function listUsers() { } // Multiple response examples #[OA\Get(path: '/users/{id}', tags: ['users'])] #[OA\Response( response: 200, description: 'User found', content: new OA\JsonContent( ref: User::class, examples: [ new OA\Examples( example: 'admin', summary: 'Admin user', value: ['id' => 1, 'name' => 'Admin', 'role' => 'admin'] ), new OA\Examples( example: 'regular', summary: 'Regular user', value: ['id' => 2, 'name' => 'John', 'role' => 'user'] ) ] ) )] public function getUser(int $id) { } } #[OA\Schema] class User { #[OA\Property] public int $id; #[OA\Property] public string $name; #[OA\Property] public string $role; } ``` ## SecurityScheme Attribute - Authentication Configuration Security schemes define authentication methods including API keys, OAuth2, HTTP bearer tokens, and OpenID Connect. ```php <?php use OpenApi\Attributes as OA; // Define security schemes at the spec level #[OA\SecurityScheme( securityScheme: 'bearerAuth', type: 'http', scheme: 'bearer', bearerFormat: 'JWT', description: 'Enter JWT Bearer token' )] #[OA\SecurityScheme( securityScheme: 'api_key', type: 'apiKey', name: 'X-API-Key', in: 'header', description: 'API key authentication' )] #[OA\SecurityScheme( securityScheme: 'oauth2', type: 'oauth2', flows: [ new OA\Flow( flow: 'authorizationCode', authorizationUrl: 'https://auth.example.com/oauth/authorize', tokenUrl: 'https://auth.example.com/oauth/token', scopes: [ 'read:users' => 'Read user information', 'write:users' => 'Modify user information', 'admin' => 'Full administrative access' ] ) ] )] class SecuritySpec { } // Apply security to operations class SecureController { // Single security scheme #[OA\Get( path: '/secure/data', security: [['bearerAuth' => []]], tags: ['secure'] )] #[OA\Response(response: 200, description: 'Secured data')] public function getSecureData() { } // OAuth2 with specific scopes #[OA\Post( path: '/users', security: [['oauth2' => ['write:users']]], tags: ['users'] )] #[OA\Response(response: 201, description: 'User created')] public function createUser() { } // Multiple security options (OR logic) #[OA\Get( path: '/flexible', security: [ ['bearerAuth' => []], ['api_key' => []] ], tags: ['secure'] )] #[OA\Response(response: 200, description: 'Success')] public function flexibleAuth() { } } // Global default security for all endpoints #[OA\OpenApi( openapi: OA\OpenApi::VERSION_3_1_0, security: [['bearerAuth' => []]] )] #[OA\Info(version: '1.0.0', title: 'Secure API')] class GlobalSecuritySpec { } ``` ## Enum Support - PHP 8.1+ Native Enums swagger-php supports PHP 8.1+ native enums, automatically generating enum schemas with proper values. ```php <?php use OpenApi\Attributes as OA; // Backed enum with string values enum Status: string { case PENDING = 'pending'; case APPROVED = 'approved'; case REJECTED = 'rejected'; } // Backed enum with integer values enum Priority: int { case LOW = 1; case MEDIUM = 2; case HIGH = 3; } // Unit enum (no backing values) #[OA\Schema] enum Color { case RED; case GREEN; case BLUE; } #[OA\Schema] class Task { #[OA\Property] public int $id; #[OA\Property] public string $title; // Enum as property type - automatically resolved #[OA\Property] public Status $status; #[OA\Property] public Priority $priority; #[OA\Property] public Color $color; } // Using enum in operation parameters class TaskController { #[OA\Get(path: '/tasks', tags: ['tasks'])] #[OA\Parameter( name: 'status', in: 'query', schema: new OA\Schema(type: Status::class) )] #[OA\Response(response: 200, description: 'Task list')] public function listTasks() { } } ``` ## Inheritance and Composition - allOf, oneOf, anyOf swagger-php supports schema composition using allOf (inheritance), oneOf (exactly one), and anyOf (one or more) for complex data structures. ```php <?php use OpenApi\Attributes as OA; // Base schema for inheritance #[OA\Schema] class BaseModel { #[OA\Property(format: 'int64')] public int $id; #[OA\Property(format: 'date-time')] public string $createdAt; #[OA\Property(format: 'date-time')] public string $updatedAt; } // Inheritance using allOf #[OA\Schema( allOf: [ new OA\Schema(ref: BaseModel::class), new OA\Schema( properties: [ new OA\Property(property: 'title', type: 'string'), new OA\Property(property: 'content', type: 'string') ] ) ] )] class Article extends BaseModel { public string $title; public string $content; } // Polymorphism with oneOf and discriminator #[OA\Schema] class Cat { #[OA\Property] public string $name; #[OA\Property] public bool $indoor; } #[OA\Schema] class Dog { #[OA\Property] public string $name; #[OA\Property] public string $breed; } #[OA\Schema( oneOf: [ new OA\Schema(ref: Cat::class), new OA\Schema(ref: Dog::class) ], discriminator: new OA\Discriminator( propertyName: 'petType', mapping: [ 'cat' => Cat::class, 'dog' => Dog::class ] ) )] class Pet { } // Response with union types class PetController { #[OA\Get(path: '/pets/{id}', tags: ['pets'])] #[OA\Response( response: 200, description: 'Pet details', content: new OA\JsonContent( oneOf: [ new OA\Schema(ref: Cat::class), new OA\Schema(ref: Dog::class) ] ) )] public function getPet(int $id) { } } ``` ## File Upload - Multipart Form Data swagger-php supports file upload endpoints with multipart/form-data encoding and binary content types. ```php <?php use OpenApi\Attributes as OA; class FileController { // Single file upload #[OA\Post(path: '/upload', operationId: 'uploadFile', tags: ['files'])] #[OA\RequestBody( description: 'File to upload', required: true, content: new OA\MediaType( mediaType: 'multipart/form-data', schema: new OA\Schema( required: ['file'], properties: [ new OA\Property( property: 'file', description: 'File to upload', type: 'string', format: 'binary' ), new OA\Property( property: 'description', description: 'File description', type: 'string' ) ] ) ) )] #[OA\Response( response: 201, description: 'File uploaded', content: new OA\JsonContent( properties: [ new OA\Property(property: 'id', type: 'integer'), new OA\Property(property: 'filename', type: 'string'), new OA\Property(property: 'size', type: 'integer') ] ) )] public function upload() { } // Multiple file upload #[OA\Post(path: '/upload/multiple', tags: ['files'])] #[OA\RequestBody( content: new OA\MediaType( mediaType: 'multipart/form-data', schema: new OA\Schema( properties: [ new OA\Property( property: 'files', type: 'array', items: new OA\Items(type: 'string', format: 'binary') ) ] ) ) )] #[OA\Response(response: 201, description: 'Files uploaded')] public function uploadMultiple() { } // Binary stream upload #[OA\Post(path: '/upload/stream', tags: ['files'])] #[OA\RequestBody( content: new OA\MediaType( mediaType: 'application/octet-stream', schema: new OA\Schema(type: 'string', format: 'binary') ) )] #[OA\Response(response: 201, description: 'Stream uploaded')] public function uploadStream() { } } ``` ## Vendor Extensions - Custom x- Properties Vendor extensions allow adding custom metadata to any OpenAPI element using the `x` parameter. These are preserved in the output with `x-` prefix. ```php <?php use OpenApi\Attributes as OA; #[OA\OpenApi(openapi: OA\OpenApi::VERSION_3_1_0)] #[OA\Info( version: '1.0.0', title: 'Extended API', x: [ 'logo' => ['url' => 'https://example.com/logo.png'], 'tagGroups' => [ ['name' => 'User Management', 'tags' => ['users', 'auth']], ['name' => 'Content', 'tags' => ['posts', 'comments']] ] ] )] class ExtendedSpec { } class ExtendedController { #[OA\Get( path: '/internal/status', tags: ['internal'], x: [ 'internal' => true, 'rateLimit' => ['requests' => 100, 'period' => '1m'], 'codeSamples' => [ ['lang' => 'curl', 'source' => 'curl -X GET https://api.example.com/internal/status'] ] ] )] #[OA\Response(response: 200, description: 'Status')] public function status() { } } #[OA\Schema( x: [ 'examples' => [ 'basic' => ['id' => 1, 'name' => 'Example'] ] ] )] class ExtendedModel { #[OA\Property(x: ['deprecated-reason' => 'Use newField instead'])] public string $oldField; } ``` ## Processor Configuration - Customizing Generation Processors can be configured to modify how the OpenAPI specification is generated, including operation ID formatting, path filtering, and cleanup options. ```php <?php require('vendor/autoload.php'); // Configure via Generator $openapi = (new \OpenApi\Generator()) ->setConfig([ // Use hashed operation IDs instead of clear text 'operationId.hash' => true, // Filter paths by tags (regex patterns) 'pathFilter.tags' => ['/pets/', '/users/'], // Filter by path patterns 'pathFilter.paths' => ['/api\/v1\/.*/'], // Clean unused components 'cleanUnusedComponents.enabled' => true, // Preserve all tags even if unused 'augmentTags.whitelist' => ['*'], // Allow multiple @OA\Components to be merged 'mergeIntoOpenApi.mergeComponents' => true, // Augment operation parameters from docblocks 'augmentParameters.augmentOperationParameters' => true, ]) ->generate(['/path/to/api']); echo $openapi->toYaml(); ``` ## Custom Response Classes - Reusable Responses Create custom response classes to reduce boilerplate and ensure consistency across your API. ```php <?php use OpenApi\Attributes as OA; // Custom reusable response classes #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class NotFoundResponse extends OA\Response { public function __construct(string $resource = 'Resource') { parent::__construct( response: 404, description: "{$resource} not found", content: new OA\JsonContent( properties: [ new OA\Property(property: 'error', type: 'string', example: "{$resource} not found"), new OA\Property(property: 'code', type: 'integer', example: 404) ] ) ); } } #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class ValidationErrorResponse extends OA\Response { public function __construct() { parent::__construct( response: 422, description: 'Validation error', content: new OA\JsonContent( properties: [ new OA\Property(property: 'message', type: 'string'), new OA\Property( property: 'errors', type: 'object', additionalProperties: new OA\AdditionalProperties( type: 'array', items: new OA\Items(type: 'string') ) ) ] ) ); } } // Using custom responses class UserController { #[OA\Get(path: '/users/{id}', tags: ['users'])] #[OA\Response(response: 200, description: 'User found', content: new OA\JsonContent(ref: User::class))] #[NotFoundResponse('User')] public function getUser(int $id) { } #[OA\Post(path: '/users', tags: ['users'])] #[OA\RequestBody(content: new OA\JsonContent(ref: User::class))] #[OA\Response(response: 201, description: 'User created')] #[ValidationErrorResponse] public function createUser() { } } ``` ## Summary swagger-php is ideal for PHP developers building RESTful APIs who need to generate OpenAPI-compliant documentation. Common use cases include auto-generating interactive API documentation for Swagger UI or ReDoc, creating API contracts for client SDK generation, maintaining documentation that stays synchronized with code, and integrating with API gateways that consume OpenAPI specifications. The library works well with popular PHP frameworks like Laravel, Symfony, and Slim, and can be integrated into CI/CD pipelines for automated documentation generation. Integration patterns typically involve adding attributes directly to existing controllers and models, then either generating static documentation files during build/deploy or serving dynamic documentation via a dedicated endpoint. For large projects, consider using custom response classes and reusable schema components to reduce duplication. The processor pipeline allows for advanced customization including filtering endpoints by tags, cleaning unused components, and adding custom vendor extensions. The library's support for PHP 8+ attributes makes it particularly elegant for modern PHP applications, while the programmatic API through the Generator class provides flexibility for complex documentation requirements.