radebatz/openapi-extras
Composer 安装命令:
composer require radebatz/openapi-extras
包简介
Extra annotations for OpenApi/swagger-php.
关键字:
README 文档
README
Introduction
Extra attributes/annotations and other bits for swagger-php.
Installation
You can use composer or simply download the release.
Composer
The preferred method is via composer. Follow the installation instructions if you do not already have composer installed.
Once composer is installed, execute the following command in your project root to install this library:
composer require radebatz/openapi-extras
Registering the library
Use of the included annotations/attributes requires registration of a custom swagger-php processor.
Also, in the case of annotations, the registration of custom aliases / namespaces needs to be done manually.
Using the Builder
When using the OpenApiBuilder no additional registration code is required as the builder will always
configure the required MergeControllerDefaults processor.
<?php use Radebatz\OpenApi\Extras\OpenApiBuilder; $generator = (new OpenApiBuilder())->build(); // ...
Register library for attributes
<?php use OpenApi\Generator; use OpenApi\Processors\BuildPaths; use Radebatz\OpenApi\Extras\Processors\MergeControllerDefaults; $generator = new Generator(); $generator->getProcessorPipeline() ->insert(new MergeControllerDefaults(), BuildPaths::class); // ...
Register library for annotations
<?php use OpenApi\Generator; use OpenApi\Processors\BuildPaths; use Radebatz\OpenApi\Extras\Processors\MergeControllerDefaults; $namespace = 'Radebatz\\OpenApi\\Extras\\Annotations'; $generator = new Generator(); $generator ->addNamespace($namespace . '\\') ->addAlias('oax', $namespace), ->getProcessorPipeline() ->insert(new MergeControllerDefaults(), BuildPaths::class); // ...
Basic usage
OpenApiBuilder
The builder aims to simplify configuring the swagger-php Generator class by implementing
explicit methods to configure all default processors.
Futhermore, it also adds a new Customizer processor which allows to pre-process all instances
of a given OpenApi annotation/attribute by registering callbacks.
<?php declare(strict_types=1); use OpenApi\Annotations as OA; use Psr\Log\NullLogger; use Radebatz\OpenApi\Extras\OpenApiBuilder; $generator = (new OpenApiBuilder()) ->addCustomizer(OA\Info::class, fn (OA\Info $info) => $info->description = 'Foo') ->tagsToMatch(['admin']) ->clearUnusedComponents(enabled: true) ->operationIdHashing(enabled: false) ->pathsToMatch(['/api']) ->enumDescription() ->build(new NullLogger());
Controller
The controller annotation may be used to:
- add an optional url prefix to all operations in the class
- add one or more
Tags to all operations in the class - share one or more
Responses across all operations - share one or more
Header's across all operations - share one or more
Middleware's across all operations
Example for adding the /foo prefix and a 403 response to all operations in the MyController class.
<?php declare(strict_types=1); use OpenApi\Attributes as OAT; use Radebatz\OpenApi\Extras\Attributes as OAX; #[OAX\Controller(prefix: '/foo')] #[OAT\Response(response: 403, description: 'Not allowed')] class PrefixedController { #[OAT\Get(path: '/prefixed', operationId: 'prefixed')] #[OAT\Response(response: 200, description: 'All good')] public function prefixed(): mixed { return 'prefixed'; } }
Inheritance
Controller annotations are inherited from parent classes. This allows defining shared configuration on a base controller:
- Prefixes concatenate: parent
/api/v2+ child/users=/api/v2/users - Tags merge (deduplicated)
- Responses merge by response code (child overrides parent for same code)
- Headers merge by header name (child overrides parent for same name)
- Middlewares merge by exact name (deduplicated)
Set inherit: false on a child controller to opt out of inheritance.
<?php declare(strict_types=1); use OpenApi\Attributes as OAT; use Radebatz\OpenApi\Extras\Attributes as OAX; #[OAX\Controller(prefix: '/api/v2', tags: ['api'])] #[OAT\Response(response: 403, description: 'Not allowed')] #[OAX\Middleware([AuthMiddleware::class])] abstract class BaseController { } #[OAX\Controller(prefix: '/users', tags: ['users'])] #[OAT\Response(response: 404, description: 'Not found')] class UserController extends BaseController { #[OAT\Get(path: '/list', operationId: 'listUsers')] #[OAT\Response(response: 200, description: 'All good')] public function list(): mixed { // effective path: /api/v2/users/list // effective tags: ['api', 'users'] // effective responses: 200, 403 (from parent), 404 (from child) return 'list'; } }
Middleware
Middleware annotations allow to attach a list of middleware names either individually or across all operations (via the Controller annotation).
Controller-level middlewares are merged onto all operations in the class; operation-level middlewares are additive.
This is used by the openapi-router project to configure routing middleware from OpenAPI specs.
<?php declare(strict_types=1); use OpenApi\Attributes as OAT; use Radebatz\OpenApi\Extras\Attributes as OAX; #[OAX\Controller( middlewares: [new OAX\Middleware([FooMiddleware::class])] )] class MiddlewareController { #[OAT\Get(path: '/mw', operationId: 'mw')] #[OAT\Response(response: 200, description: 'All good')] #[OAX\Middleware([BarMiddleware::class])] public function mw() { return 'mw'; } }
ProvidesCustomizersInterface
Middleware subclasses can implement ProvidesCustomizersInterface to automatically apply OpenAPI modifications to operations that carry them. This eliminates duplication — for example, a jwt-auth middleware can auto-inject the security scheme without every operation declaring it manually.
<?php declare(strict_types=1); use OpenApi\Annotations as OA; use Radebatz\OpenApi\Extras\Attributes\Middleware; use Radebatz\OpenApi\Extras\ProvidesCustomizersInterface; #[\Attribute(\Attribute::TARGET_ALL | \Attribute::IS_REPEATABLE)] class SecureMiddleware extends Middleware implements ProvidesCustomizersInterface { public function __construct() { parent::__construct(names: ['jwt-auth']); } public static function customizers(): array { return [ OA\Operation::class => [ fn (OA\Operation $op) => $op->security = [['bearerAuth' => []]], ], ]; } }
Then use it on a controller — all operations inherit both the middleware name and the security effect:
<?php declare(strict_types=1); use OpenApi\Attributes as OAT; use Radebatz\OpenApi\Extras\Attributes as OAX; #[OAX\Controller(middlewares: [new SecureMiddleware()])] class UserController { #[OAT\Get(path: '/users', operationId: 'listUsers')] #[OAT\Response(response: 200, description: 'All good')] public function list(): mixed { // security: [['bearerAuth' => []]] is applied automatically return 'list'; } }
The customizers() method returns the same mapping format as the Customizers processor (annotation class => callable[]), but these are scoped — they only apply to operations carrying the middleware, not globally.
JsonResponse
A shorthand for JSON responses that reference a schema. Wraps the referenced schema in an envelope property (default "data", matching Laravel's JsonResource::$wrap). For unwrapped responses, use the regular OAT\Response annotation.
If no description is provided, it is derived from the referenced schema (fallback order: title > description > schema name > class short name).
| Parameter | Default | Effect |
|---|---|---|
wrap |
'data' |
Property name for the envelope |
<?php declare(strict_types=1); use OpenApi\Attributes as OAT; use Radebatz\OpenApi\Extras\Attributes as OAX; #[OAT\Schema(schema: 'TokenPairResource', title: 'Token pair')] class TokenPairResource { #[OAT\Property(property: 'access_token', type: 'string')] public string $accessToken; #[OAT\Property(property: 'refresh_token', type: 'string')] public string $refreshToken; } class AuthController { #[OAT\Post(path: '/auth/login', operationId: 'login')] // Wrapped (default): {"data": {$ref: TokenPairResource}} #[OAX\JsonResponse(response: 200, ref: TokenPairResource::class)] #[OAX\JsonResponse(response: 401, description: 'Invalid credentials')] public function login(): mixed { return '...'; } #[OAT\Get(path: '/auth/session', operationId: 'session')] // Custom wrap key: {"result": {$ref: TokenPairResource}} #[OAX\JsonResponse(response: 200, ref: TokenPairResource::class, wrap: 'result')] public function session(): mixed { return '...'; } #[OAT\Get(path: '/auth/tokens', operationId: 'tokens')] // List: {"data": [{$ref: TokenPairResource}, ...]} #[OAX\JsonResponse( response: 200, content: new OAT\JsonContent(type: 'array', items: new OAT\Items(ref: TokenPairResource::class)), )] public function tokens(): mixed { return '...'; } }
This generates:
content: application/json: schema: required: [data] properties: data: $ref: '#/components/schemas/TokenPairResource'
JsonRequestBody
A shorthand for JSON request bodies that reference a schema. Reduces nesting by wrapping the ref in a JsonContent automatically.
If no description is provided, it is derived from the referenced schema (fallback order: title > description > schema name > class short name).
<?php declare(strict_types=1); use OpenApi\Attributes as OAT; use Radebatz\OpenApi\Extras\Attributes as OAX; #[OAT\Schema(schema: 'LoginRequest', title: 'Login credentials')] class LoginRequest { #[OAT\Property(property: 'email', type: 'string')] public string $email; #[OAT\Property(property: 'password', type: 'string')] public string $password; } class AuthController { // Method-level with explicit ref #[OAT\Post(path: '/auth/login', operationId: 'login')] #[OAX\JsonRequestBody(ref: LoginRequest::class, required: true)] public function login(): mixed { // description auto-derived as "Login credentials" from schema title return '...'; } // Parameter-level — ref inferred from type-hint #[OAT\Post(path: '/auth/register', operationId: 'register')] public function register(#[OAX\JsonRequestBody] LoginRequest $request): mixed { return '...'; } }
This is equivalent to the more verbose:
#[OAT\RequestBody( description: 'Login credentials', required: true, content: new OAT\JsonContent(ref: LoginRequest::class) )]
When ref points to a class with an #[OAT\RequestBody] annotation, a component $ref is generated instead of inline JsonContent:
#[OAT\RequestBody(request: 'SharedCreateBody')] class SharedCreateBody { /* ... */ } class ItemController { #[OAT\Post(path: '/items', operationId: 'createItem')] #[OAX\JsonRequestBody(ref: SharedCreateBody::class)] public function create(): mixed { // produces: $ref: '#/components/requestBodies/SharedCreateBody' return '...'; } }
License
The openapi-extras project is released under the MIT license.
radebatz/openapi-extras 适用场景与选型建议
radebatz/openapi-extras 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 15.22k 次下载、GitHub Stars 达 2, 最近一次更新时间为 2022 年 12 月 12 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「api」 「openapi」 「Swagger-PHP」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 radebatz/openapi-extras 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 radebatz/openapi-extras 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 radebatz/openapi-extras 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Swagger for Laravel 5
Laravel Swagger generator for the Model
Provides possibility for validate data according to Swagger documentation
Swagger-php integration package for Spiral Framework.
Swagger-php integration package for Spiral Framework.
A PSR-7 compatible library for making CRUD API endpoints
统计信息
- 总下载量: 15.22k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 3
- 点击次数: 18
- 依赖项目数: 1
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2022-12-12