English | 中文
基于 Hyperf 框架的 Swagger/OpenAPI 文档自动生成组件,支持 Swoole/Swow 引擎,为您提供优雅的 API 文档解决方案。
- 🚀 自动生成 - 基于 PHP 8 Attributes 自动生成 OpenAPI 3.0/3.1 文档
- 🎯 类型安全 - 支持 DTO 模式,参数自动映射到 PHP 类
- 📝 多种 UI - 支持 Swagger UI、Knife4j、Redoc、RapiDoc、Scalar 等多种文档界面
- ✅ 数据验证 - 集成 Hyperf 验证器,支持丰富的验证注解
- 🔒 安全认证 - 支持 API Token 和多种安全方案
- 🔄 类型支持 - 支持数组、递归、嵌套、枚举等复杂类型
- 🎨 灵活配置 - 可自定义全局响应格式、路由前缀等
- 📦 开箱即用 - 零配置即可使用,同时支持深度定制
- PHP >= 8.2
- Hyperf ~3.2
- Swoole >= 5.0 或 Swow
- 控制器方法尽可能返回具体的类(包含简单类型),这样能更好地生成文档
- 当返回类无法满足需求时,可使用
#[ApiResponse]注解补充
composer require tangwei/apidocs默认使用 Swagger UI,推荐安装 Knife4j UI(可选):
composer require tangwei/knife4j-uiphp bin/hyperf.php vendor:publish tangwei/apidocs配置文件将发布到 config/autoload/api_docs.php
<?php// config/autoload/api_docs.phpreturn [
// 启用文档服务(建议生产环境禁用)'enable' => env('APP_ENV') !== 'prod',
// 文档访问路径'prefix_url' => env('API_DOCS_PREFIX_URL', '/swagger'),
// 基础信息'swagger' => [
'info' => [
'title' => 'API 文档',
'version' => '1.0.0',
'description' => '项目 API 文档',
],
'servers' => [
[
'url' => 'http://127.0.0.1:9501',
'description' => 'API 服务器',
],
],
],
];完整配置文件示例:config/autoload/api_docs.php
完整配置说明(点击展开)
useHyperf\ApiDocs\DTO\GlobalResponse;
usefunctionHyperf\Support\env;
return [
/* |-------------------------------------------------------------------------- | 启动 swagger 服务 |-------------------------------------------------------------------------- | | false 将不会启动 swagger 服务 | */'enable' => env('APP_ENV') !== 'prod',
/* |-------------------------------------------------------------------------- | 生成swagger文件格式 |-------------------------------------------------------------------------- | | 支持json和yaml | */'format' => 'json',
/* |-------------------------------------------------------------------------- | 生成swagger文件路径 |-------------------------------------------------------------------------- */'output_dir' => BASE_PATH . '/runtime/container',
/* |-------------------------------------------------------------------------- | 生成代理类路径 |-------------------------------------------------------------------------- */'proxy_dir' => BASE_PATH . '/runtime/container/proxy',
/* |-------------------------------------------------------------------------- | 设置路由前缀 |-------------------------------------------------------------------------- */'prefix_url' => env('API_DOCS_PREFIX_URL', '/swagger'),
/* |-------------------------------------------------------------------------- | 设置swagger资源路径,cdn资源 |-------------------------------------------------------------------------- */'prefix_swagger_resources' => 'https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.27.1',
/* |-------------------------------------------------------------------------- | 设置全局返回的代理类 |-------------------------------------------------------------------------- | | 全局返回 如:[code=>200,data=>null] 格式,设置会后会全局生成对应文档 | 配合ApiVariable注解使用,示例参考GlobalResponse类 | 返回数据格式可以利用AOP统一返回 | */// 'global_return_responses_class' => GlobalResponse::class,/* |-------------------------------------------------------------------------- | 替换验证属性 |-------------------------------------------------------------------------- | | 通过获取注解ApiModelProperty的值,来提供数据验证的提示信息 | */'validation_custom_attributes' => true,
/* |-------------------------------------------------------------------------- | 设置DTO类默认值等级 |-------------------------------------------------------------------------- | | 设置:0 默认(不设置默认值) | 设置:1 简单类型会为设置默认值,复杂类型(带?)会设置null | - 简单类型默认值: int:0 float:0 string:'' bool:false array:[] mixed:null | 设置:2 (慎用)包含等级1且复杂类型(联合类型除外)会设置null | */'dto_default_value_level' => 0,
/* |-------------------------------------------------------------------------- | 全局responses,映射到ApiResponse注解对象 |-------------------------------------------------------------------------- */'responses' => [
['response' => 401, 'description' => 'Unauthorized'],
['response' => 500, 'description' => 'System error'],
],
/* |-------------------------------------------------------------------------- | swagger 的基础配置 |-------------------------------------------------------------------------- | | 该属性会映射到OpenAPI对象 | */'swagger' => [
'info' => [
'title' => 'API DOC',
'version' => '0.1',
'description' => 'swagger api desc',
],
'servers' => [
[
'url' => 'http://127.0.0.1:9501',
'description' => 'OpenApi host',
],
],
'components' => [
'securitySchemes' => [
[
'securityScheme' => 'Authorization',
'type' => 'apiKey',
'in' => 'header',
'name' => 'Authorization',
],
],
],
'security' => [
['Authorization' => []],
],
'externalDocs' => [
'description' => 'Find out more about Swagger',
'url' => 'https://github.com/tw2066/api-docs',
],
],
];php bin/hyperf.php start[INFO] Swagger docs url at http://0.0.0.0:9501/swagger
[INFO] Worker#0 started.
[INFO] HTTP Server listening at 0.0.0.0:9501
- 启动成功后,访问
http://your-host:9501/swagger即可查看 API 文档。 - 访问
http://your-host:9501/swagger/llms.txt包含控制器每个Markdown页面的链接,可以用于Ai快速访问编程文档。 - 其他服务访问
http://your-host:9501/swagger/{service-name}.md访问{service-name}服务的 Markdown 文档。
<?phpnamespaceApp\Request;
useHyperf\ApiDocs\Annotation\ApiModelProperty;
useHyperf\DTO\Annotation\Validation\Required;
useHyperf\DTO\Annotation\Validation\Integer;
useHyperf\DTO\Annotation\Validation\Between;
class UserRequest
{
#[ApiModelProperty('用户名')]
#[Required]
publicstring$username;
#[ApiModelProperty('年龄')]
#[Required]
#[Integer]
#[Between(1, 120)]
publicint$age;
#[ApiModelProperty('邮箱')]
public ?string$email = null;
}<?phpnamespaceApp\Controller;
useHyperf\HttpServer\Annotation\Controller;
useHyperf\HttpServer\Annotation\GetMapping;
useHyperf\HttpServer\Annotation\PostMapping;
useHyperf\ApiDocs\Annotation\Api;
useHyperf\ApiDocs\Annotation\ApiOperation;
useHyperf\DTO\Annotation\Contracts\RequestBody;
useHyperf\DTO\Annotation\Contracts\RequestQuery;
useHyperf\DTO\Annotation\Contracts\Valid;
useApp\Request\UserRequest;
#[Controller(prefix: '/user')]
#[Api(tags: '用户管理', position: 1)]
class UserController
{
#[GetMapping(path: 'list')]
#[ApiOperation(summary: '获取用户列表')]
publicfunctionlist(#[RequestQuery] #[Valid] UserRequest$request): array
{
return [
['id' => 1, 'username' => 'admin'],
['id' => 2, 'username' => 'user'],
];
}
#[PostMapping(path: 'create')]
#[ApiOperation(summary: '创建用户')]
publicfunctioncreate(#[RequestBody] #[Valid] UserRequest$request): array
{
return [
'id' => 1,
'username' => $request->username,
'age' => $request->age,
];
}
}#[Api(
tags: '用户管理', // 标签名称(支持数组)
description: '用户相关操作', // 描述
position: 1, // 排序位置
hidden: false// 是否隐藏
)]#[ApiOperation(
summary: '创建用户', // 摘要
description: '详细描述', // 详细描述
deprecated: false, // 是否废弃
security: true, // 是否需要认证
hidden: false// 是否隐藏
)]// 简单类型响应
#[ApiResponse(PhpType::INT, 200, '成功')]
// 对象响应
#[ApiResponse(UserResponse::class, 200, '用户信息')]
// 数组响应
#[ApiResponse([UserResponse::class], 200, '用户列表')]
// 分页响应
#[ApiResponse(newPage([UserResponse::class]), 200, '分页数据')]泛型支持示例:
PHP 暂不支持泛型,可通过 #[ApiVariable] 实现:
useHyperf\ApiDocs\Annotation\ApiVariable;
class Page
{
publicint$total;
#[ApiVariable]
publicarray$content;
publicfunction__construct(array$content, int$total = 0)
{
$this->content = $content;
$this->total = $total;
}
}控制器使用:
#[ApiOperation('分页查询')]
#[GetMapping(path: 'page')]
#[ApiResponse(newPage([UserResponse::class]))]
publicfunctionpage(#[RequestQuery] PageQuery$query): Page
{
// 返回分页数据
}获取 POST/PUT/PATCH 请求的 JSON body 参数:
publicfunctioncreate(#[RequestBody] #[Valid] UserRequest$request)
{
// $request 自动填充 body 数据
}获取 URL 查询参数(GET 参数):
publicfunctionlist(#[RequestQuery] #[Valid] QueryRequest$request)
{
// $request 自动填充查询参数
}获取表单数据(multipart/form-data):
#[ApiFormData(name: 'photo', format: 'binary')]
publicfunctionupload(#[RequestFormData] UploadRequest$formData)
{
$file = $this->request->file('photo');
// 处理文件上传
}获取请求头信息:
publicfunctionauth(#[RequestHeader] #[Valid] AuthHeader$header)
{
// $header 自动填充请求头数据
}
⚠️ 注意:一个方法不能同时注入RequestBody和RequestFormData
#[ApiModelProperty(
value: '用户名', // 属性描述
example: 'admin', // 示例值
required: true, // 是否必填
hidden: false// 是否隐藏
)]
public string $username;// 全局请求头(类级别)
#[ApiHeader('X-Request-Id')]
// 方法级请求头
#[ApiHeader(
name: 'Authorization',
required: true,
type: 'string',
description: 'Bearer token'
)]优先级:方法 > 类 > 全局
// 使用默认认证
#[ApiSecurity('Authorization')]
// 方法级覆盖
#[ApiOperation(summary: '登录', security: false)] // 不需要认证组件提供丰富的验证注解支持:
useHyperf\DTO\Annotation\Validation\*;
class UserRequest
{
#[Required] // 必填
#[Max(50)] // 最大长度
public string $username;
#[Required]
#[Integer] // 整数
#[Between(1, 120)] // 范围
public int $age;
#[Email] // 邮箱格式
public ?string $email;
#[Url] // URL 格式
public ?string $website;
#[Regex('/^1[3-9]\d{9}$/')] // 正则验证
public ?string $mobile;
#[In(['male', 'female'])] // 枚举值
public ?string $gender;
#[Date] // 日期格式
public ?string $birthday;
}💡 提示:只需在控制器方法参数中添加
#[Valid]注解即可启用验证
publicfunctioncreate(#[RequestBody] #[Valid] UserRequest$request)
{
// 验证自动执行
}// 支持 Laravel 风格的验证规则
#[Validation('required|string|min:3|max:50')]
public string $username;
// 数组元素验证
#[Validation('integer', customKey: 'ids.*')]
public array $ids;<?phpnamespaceApp\Validation;
useAttribute;
useHyperf\DTO\Annotation\Validation\BaseValidation;
#[Attribute(Attribute::TARGET_PROPERTY)]
class Mobile extends BaseValidation
{
protected$rule = 'regex:/^1[3-9]\d{9}$/';
publicfunction__construct(string$messages = '手机号格式错误')
{
parent::__construct($messages);
}
}使用自定义验证:
useApp\Validation\Mobile;
class RegisterRequest
{
#[Required]
#[Mobile]
publicstring$phone;
}/** * @var Address[] */
#[ApiModelProperty('地址列表')]
public array $addresses;
/** * @var int[] */
#[ApiModelProperty('ID 列表')]
public array $ids;useHyperf\DTO\Annotation\ArrayType;
#[ApiModelProperty('地址列表')]
#[ArrayType(Address::class)]
public array $addresses;
#[ApiModelProperty('标签列表')]
#[ArrayType('string')]
public array $tags;class UserRequest
{
publicstring$name;
// 嵌套对象
#[ApiModelProperty('地址信息')]
publicAddress$address;
/** * @var Address[] */
#[ApiModelProperty('多个地址')]
publicarray$addresses;
}
class Address
{
publicstring$province;
publicstring$city;
publicstring$street;
}useHyperf\DTO\Type\PhpType;
enum StatusEnum: int
{
casePENDING = 0;
caseACTIVE = 1;
caseINACTIVE = 2;
}
class OrderRequest
{
#[ApiModelProperty('订单状态')]
publicStatusEnum$status;
}配置全局响应包装类:
// config/autoload/api_docs.phpreturn [
'global_return_responses_class' => \App\DTO\GlobalResponse::class,
];定义全局响应类:
<?phpnamespaceApp\DTO;
useHyperf\ApiDocs\Annotation\ApiModelProperty;
useHyperf\ApiDocs\Annotation\ApiVariable;
class GlobalResponse
{
#[ApiModelProperty('状态码')]
publicint$code = 0;
#[ApiModelProperty('消息')]
publicstring$message = 'success';
#[ApiVariable]
#[ApiModelProperty('响应数据')]
publicmixed$data = null;
}#[PostMapping(path: 'upload')]
#[ApiOperation(summary: '文件上传')]
#[ApiFormData(name: 'file', format: 'binary', required: true)]
#[ApiFormData(name: 'description', type: 'string')]
publicfunctionupload(#[RequestFormData] UploadRequest$request)
{
$file = $this->request->file('file');
// 处理文件上传return ['url' => '/uploads/file.jpg'];
}访问不同的 UI 界面:
- Swagger UI:
http://your-host:9501/swagger - Knife4j:
http://your-host:9501/swagger/doc(需安装tangwei/knife4j-ui) - Redoc:
http://your-host:9501/swagger/redoc - RapiDoc:
http://your-host:9501/swagger/rapidoc - Scalar:
http://your-host:9501/swagger/scalar
api-docs 依赖 DTO 组件,更多详情请查看 DTO 文档
标记为 DTO 类:
useHyperf\DTO\Annotation\Dto;
#[Dto]
class DemoQuery
{
}- 可以设置返回格式
#[Dto(Convert::SNAKE)],批量转换为下划线格式的 key Dto注解不会生成文档,要生成对应文档使用JSONField注解
用于设置属性的别名:
useHyperf\DTO\Annotation\Dto;
useHyperf\DTO\Annotation\JSONField;
#[Dto]
class DemoQuery
{
#[ApiModelProperty('这是一个别名')]
#[JSONField('alias_name')]
#[Required]
publicstring$name;
}- 设置
JSONField后会生成代理类,生成alias_name属性 - 接收和返回字段都以
alias_name为准
aspects.php 中配置:
return [
\Hyperf\DTO\Aspect\ObjectNormalizerAspect::class
]当框架导入 symfony/serializer (^5.0) 和 symfony/property-access (^5.0) 后,在 dependencies.php 中配置映射关系:
useHyperf\Serializer\SerializerFactory;
useHyperf\Serializer\Serializer;
return [
Hyperf\Contract\NormalizerInterface::class => newSerializerFactory(Serializer::class),
];- 使用有意义的类名,如
CreateUserRequest、UserResponse - 为每个属性添加
ApiModelProperty注解 - 分离 Request 和 Response 定义
- 合理使用验证注解
- 使用
Api注解对控制器分组 - 为每个方法添加
ApiOperation描述 - 尽可能返回具体类型而非
array - 合理使用
ApiResponse定义响应格式
- 生产环境禁用文档服务
- 使用
ApiSecurity控制 API 认证 - 使用
hidden: true隐藏敏感接口
- 开发环境使用文档,生产环境禁用
- 合理使用缓存
- 避免过深的嵌套结构
A: 检查以下几点:
- 配置文件中
enable是否为true - 查看日志是否有错误信息
A: 使用 PHPDoc 注释或 ArrayType 注解:
/** * @var User[] */public array $users;
// 或
#[ArrayType(User::class)]
public array $users;A: 使用 hidden 参数:
#[Api(hidden: true)] // 隐藏整个控制器
#[ApiOperation(summary: '测试', hidden: true)] // 隐藏单个接口A: 使用 ApiResponse 注解或配置全局响应类:
#[ApiResponse(UserResponse::class, 200, '成功')]
publicfunctiongetUser(): UserResponse
{
returnnewUserResponse();
}A: 支持所有 Hyperf Validation 规则。详见 Hyperf 验证器文档。
A: 支持,但只会收集 POST 方法。建议使用标准路由注解以获得更好的文档生成效果。
完整示例请参考 example 目录
如果这个项目对你有帮助,请给个 ⭐ Star!