Skip to content

Automatic OpenAPI 3.0 specification generator from routes and DTOs. Extracts API documentation from code using PHP 8 attributes, Symfony Validator constraints, and PSR-15 middleware. JSON/YAML output, zero configuration. Framework-agnostic, CLI integration.

License

Notifications You must be signed in to change notification settings

MethorZ/openapi-generator

Repository files navigation

MethorZ OpenAPI Generator

Automatic OpenAPI 3.0 specification generator from routes and DTOs

PHP Version License

Automatically generates OpenAPI specifications by analyzing your application's routes and Data Transfer Objects (DTOs). Perfect for Mezzio, Laminas, and any PSR-15 application.


✨ Features

  • πŸš€ Automatic Generation: Scans routes and DTOs to generate complete OpenAPI specs
  • πŸ“ DTO Analysis: Extracts request/response schemas from PHP DTOs with property promotion
  • βœ… Validation Integration: Reads Symfony Validator attributes for schema constraints
  • 🎯 Handler Detection: Automatically finds request and response DTOs in handlers
  • πŸ“¦ Multiple Formats: Generates both YAML and JSON outputs
  • πŸ”§ Zero Configuration: Works out-of-the-box with sensible defaults
  • 🎨 Customizable: Configure via application config
  • πŸ”— Nested DTOs: Automatically generates schemas for nested DTO references
  • πŸ“š Collections: Supports typed arrays with @param array<Type> PHPDoc
  • 🎲 Enums: Full support for backed and unit enums (PHP 8.1+)
  • πŸ”€ Union Types: Generates oneOf schemas for union types (PHP 8.0+)
  • ⚑ Performance: Schema caching for efficient generation

πŸ“¦ Installation

composer require methorz/openapi-generator

πŸš€ Quick Start

1. Register the Command

Add to your application's command configuration:

// config/autoload/dependencies.global.php
use Methorz\OpenApi\Command\GenerateOpenApiCommand;

return [
    'dependencies' => [
        'factories' => [
            GenerateOpenApiCommand::class => function ($container) {
                return new GenerateOpenApiCommand($container);
            },
        ],
    ],
];

2. Generate Specification

php bin/console openapi:generate

This will create:

  • public/openapi.yaml - YAML format
  • public/openapi.json - JSON format

πŸ“– Usage

Basic Configuration

// config/autoload/openapi.global.php
return [
    'openapi' => [
        'title' => 'My API',
        'version' => '1.0.0',
    ],
];

Example Handler

The generator automatically analyzes your handlers:

namespace App\Handler;

use App\Request\CreateItemRequest;
use App\Response\ItemResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

final class CreateItemHandler implements RequestHandlerInterface
{
    public function handle(
        ServerRequestInterface $request,
        CreateItemRequest $dto // ← Request DTO detected
    ): ItemResponse {           // ← Response DTO detected
        // Handler logic...
    }
}

Example Request DTO

namespace App\Request;

use Symfony\Component\Validator\Constraints as Assert;

final readonly class CreateItemRequest
{
    public function __construct(
        #[Assert\NotBlank]
        #[Assert\Length(min: 3, max: 100)]
        public string $name,

        #[Assert\NotBlank]
        #[Assert\Length(min: 10, max: 500)]
        public string $description,

        #[Assert\Email]
        public string $email,
    ) {}
}

Generated Schema:

components:
  schemas:
    CreateItemRequest:
      type: object
      required:
        - name
        - description
        - email
      properties:
        name:
          type: string
          minLength: 3
          maxLength: 100
        description:
          type: string
          minLength: 10
          maxLength: 500
        email:
          type: string
          format: email

πŸ“‹ Supported Validation Attributes

The generator extracts constraints from Symfony Validator attributes:

Attribute OpenAPI Property
@Assert\NotBlank required: true
@Assert\Length(min, max) minLength, maxLength
@Assert\Range(min, max) minimum, maximum
@Assert\Email format: email
@Assert\Url format: uri
@Assert\Uuid format: uuid

πŸš€ Advanced Features

Enum Support

Generates enum schemas from PHP 8.1+ backed enums:

enum StatusEnum: string
{
    case DRAFT = 'draft';
    case ACTIVE = 'active';
    case ARCHIVED = 'archived';
}

final readonly class CreateItemRequest
{
    public function __construct(
        public StatusEnum $status,
    ) {}
}

Generated Schema:

CreateItemRequest:
  type: object
  properties:
    status:
      type: string
      enum: ['draft', 'active', 'archived']

Nested DTOs

Automatically generates schemas for nested DTO objects:

final readonly class AddressDto
{
    public function __construct(
        #[Assert\NotBlank]
        public string $street,

        #[Assert\NotBlank]
        public string $city,

        public ?string $country = null,
    ) {}
}

final readonly class CreateUserRequest
{
    public function __construct(
        public string $name,
        public AddressDto $address,              // ← Nested DTO
        public ?AddressDto $billingAddress = null, // ← Nullable nested DTO
    ) {}
}

Generated Schema:

CreateUserRequest:
  type: object
  required: ['name', 'address']
  properties:
    name:
      type: string
    address:
      $ref: '#/components/schemas/AddressDto'
    billingAddress:
      $ref: '#/components/schemas/AddressDto'
      nullable: true

AddressDto:
  type: object
  required: ['street', 'city']
  properties:
    street:
      type: string
    city:
      type: string
    country:
      type: string
      nullable: true

Typed Collections

Supports typed arrays using PHPDoc annotations:

/**
 * @param array<int, AddressDto> $addresses
 * @param array<string> $tags
 */
final readonly class CreateOrderRequest
{
    public function __construct(
        public string $orderId,
        public array $addresses,
        public array $tags,
    ) {}
}

Generated Schema:

CreateOrderRequest:
  type: object
  properties:
    orderId:
      type: string
    addresses:
      type: array
      items:
        $ref: '#/components/schemas/AddressDto'
    tags:
      type: array
      items:
        type: string

Union Types

Generates oneOf schemas for union types:

final readonly class FlexibleRequest
{
    public function __construct(
        public string|int $identifier,  // ← Union type
    ) {}
}

Generated Schema:

FlexibleRequest:
  type: object
  properties:
    identifier:
      oneOf:
        - type: string
        - type: integer

🎯 Features

Route Detection

Scans your application's route configuration:

// config/autoload/routes.global.php
return [
    'routes' => [
        [
            'path' => '/api/v1/items',
            'middleware' => [CreateItemHandler::class],
            'allowed_methods' => ['POST'],
        ],
    ],
];

Automatic Operation Generation

Creates OpenAPI operations with:

  • HTTP method (GET, POST, PUT, DELETE, etc.)
  • Path parameters (extracted from {id} patterns)
  • Request body (for POST/PUT/PATCH)
  • Response schemas
  • Summary and operationId
  • Tags (from module namespace)

Path Parameters

Automatically detects and types path parameters:

'/api/v1/items/{id}' β†’ parameter: id (format: uuid)
'/api/v1/users/{userId}' β†’ parameter: userId (type: integer)

πŸ“‚ Generated Output

OpenAPI Structure

openapi: 3.0.0
info:
  title: My API
  version: 1.0.0
  description: Automatically generated from routes and DTOs
servers:
  - url: http://localhost:8080
    description: Local development
paths:
  /api/v1/items:
    post:
      operationId: createItem
      summary: create item
      tags:
        - Items
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateItemRequest'
      responses:
        201:
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ItemResponse'
        400:
          description: Bad Request
        404:
          description: Not Found
components:
  schemas:
    CreateItemRequest:
      # ... schema definition
    ItemResponse:
      # ... schema definition

πŸ”§ Configuration

Full Configuration Example

// config/autoload/openapi.global.php
return [
    'openapi' => [
        'title' => 'My API',
        'version' => '1.0.0',
        'description' => 'API for managing items',
        'servers' => [
            [
                'url' => 'https://api.example.com',
                'description' => 'Production',
            ],
            [
                'url' => 'http://localhost:8080',
                'description' => 'Development',
            ],
        ],
    ],
];

πŸ“Š Integration with Swagger UI

View your generated OpenAPI specification:

# Install Swagger UI
composer require swagger-api/swagger-ui

# Access at:
http://localhost:8080/swagger-ui

Or use online tools:


πŸ§ͺ Testing

# Run all tests
composer test

# Run with coverage
composer test:coverage

# Code style check
composer cs-check

# Fix code style
composer cs-fix

# Static analysis
composer analyze

# All quality checks
composer quality

🀝 Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Write tests for new features
  4. Ensure all quality checks pass (composer quality)
  5. Submit a pull request

πŸ“„ License

MIT License. See LICENSE for details.


πŸ”— Related Packages


πŸ™ Acknowledgments

Built with:


πŸ“ž Support


Made with ❀️ by Thorsten Merz

About

Automatic OpenAPI 3.0 specification generator from routes and DTOs. Extracts API documentation from code using PHP 8 attributes, Symfony Validator constraints, and PSR-15 middleware. JSON/YAML output, zero configuration. Framework-agnostic, CLI integration.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages