资讯详情 PHP8.5怎么使用Swagger生成API文档
📅 2026/10/3 3:12:35
前言先说清楚一个命名问题Swagger 这个词今天有两层含义一是规范本身Swagger Specification 已更名为 OpenAPI Specification简称 OAS当前主流版本是 3.1二是 Swagger 官方那套 UI 与编辑器工具。所以用 Swagger 生成 API 文档在 PHP 里的标准做法是用zircote/swagger-php这个库从代码里的 PHP 属性AttributePHP 8.0 引入的#[...]语法扫描出符合 OpenAPI 规范的 JSON/YAML 文件再把这份文件交给任意支持 OpenAPI 的界面去渲染。那么这件事和 PHP 8.5 有什么关系关系有两点一是属性语法本身要求 PHP 8.0 起步8.5 完全满足二是依赖库的composer.json里往往写着php: ^8.1这类约束上限在 8.5 上执行composer require时可能因为平台版本检查直接报错。这是升级到 8.5 之后第一个会撞上的问题本文会给出排查方法。此外本文示例会用到 PHP 8.5 引入的管道操作符pipe operator写作|来串一趟小工具的逻辑。一、工具链选型生成与渲染分离PHP 生态里生成文档的路径很清晰不要把它们混在一起环节可选方案说明从代码提取接口描述zircote/swagger-php扫描 PHP 属性或注解输出 OpenAPI JSON/YAML手写规范文件直接写openapi.yaml接口少、变动少时更省事但容易和代码脱节渲染成可交互页面Swagger UI、Redoc 等任意 OpenAPI 3.x 渲染器只要它能读取你的 JSON/YAML 即可校验规范合法性生成流程中的校验开关或独立校验工具建议接入 CI防止规范文件写错导致前端无法生成 SDK选型建议接口多于三五个、且由团队共同维护时用代码生成只有一两个静态接口时手写 YAML 更省时间。本文按代码生成的方式讲。安装composer require --dev zircote/swagger-php这里就会遇到 PHP 8.5 的第一个坑如果报形如requires php ^8.1 - your php version (8.5.x) does not satisfy that requirement的错误说明库的版本约束还没加上 8.5。处理方式有两种优先第一种# 1) 先升级到该库的最新大版本新版本通常已放开版本上限 composer require --dev zircote/swagger-php:^5.0 # 2) 确实没有兼容版本时临时忽略平台检查仅用于验证不建议长期留在生产依赖里 composer require --dev --ignore-platform-reqphp zircote/swagger-php二、用属性描述接口核心思路接口的描述和接口实现放在同一个文件里代码改了文档跟着改。下面是一个完整可扫描的控制器示例。注意命名空间别名OpenApi\Attributes as OA是约定俗成的写法。?php declare(strict_types1); // PHP 8.5 zircote/swagger-php 4.x 或 5.x namespace App\Controller; use OpenApi\Attributes as OA; #[OA\Info( version: 1.0.0, title: 订单服务 API, description: 订单创建与查询接口OpenAPI 3.1 描述, contact: new OA\Contact(email: apiexample.com) )] // 服务器地址用相对路径规范文件由哪个域名提供服务就解析到哪个域名 #[OA\Server(url: /api/v1, description: 生产环境相对当前域名)] #[OA\Tag(name: orders, description: 订单相关接口)] class OrderController { /** * 创建订单 */ #[OA\Post( path: /api/orders, summary: 创建订单, tags: [orders], requestBody: new OA\RequestBody( required: true, content: new OA\JsonContent( required: [sku, quantity], properties: [ new OA\Property(property: sku, type: string, example: SKU-1001), new OA\Property(property: quantity, type: integer, minimum: 1, example: 2), ], type: object ) ), responses: [ new OA\Response( response: 201, description: 创建成功, content: new OA\JsonContent( properties: [ new OA\Property(property: id, type: integer, example: 1024), new OA\Property(property: status, type: string, example: pending), ], type: object ) ), new OA\Response(response: 422, description: 参数校验失败), ] )] public function create(): array { // 真实的业务实现写在这里 return [id 1024, status pending]; } /** * 查询订单 */ #[OA\Get( path: /api/orders/{id}, summary: 按 ID 查询订单, tags: [orders], parameters: [ new OA\PathParameter( name: id, description: 订单 ID, required: true, schema: new OA\Schema(type: integer, minimum: 1) ), ], responses: [ new OA\Response(response: 200, description: 订单详情), new OA\Response(response: 404, description: 订单不存在), ] )] public function show(int $id): array { return [id $id, status pending]; } }几个必须注意的写法细节属性里的嵌套对象要写new OA\Xxx(...)不能写成OA\Xxx(...)这种类似函数调用的形式。responses是数组每一项是一整个new OA\Response(...)response参数用整数或字符串都可以推荐写200这种整数输出时会被转成200。type: object这类固定值容易漏。OA\JsonContent只要写了properties就应当补上type: object否则规范校验时会报结构不完整。一个类上的OA\Info只允许出现一次重复会导致生成失败。三、生成与校验安装完成后vendor/bin/openapi就是生成入口# 扫描 src/ 目录输出到 openapi.yaml ./vendor/bin/openapi --output openapi.yaml src/ # 输出 JSON 格式更利于程序读取 ./vendor/bin/openapi --output openapi.json src/ # 多个目录、排除测试代码 ./vendor/bin/openapi --output openapi.json src/ --exclude tests/上面这些是zircote/swagger-php命令行工具的常规用法不同大版本之间选项会有增减执行前先用./vendor/bin/openapi --help确认当前版本支持哪些开关是否提供校验模式也以官方文档为准。生成的 YAML 片段大概长这样openapi: 3.1.0 info: title: 订单服务 API version: 1.0.0 paths: /api/orders: post: summary: 创建订单 tags: [orders] responses: 201: description: 创建成功把生成命令写进 CI 或发版脚本是保证文档不腐化的关键——文档一旦需要记得手动更新就一定会过期。# 发布前重新生成并用 git 检查是否有未提交的差异 ./vendor/bin/openapi --output openapi.json src/ git diff --exit-code openapi.json || echo 接口描述已变更请提交更新后的规范文件四、把规范接进 PHP 应用最省事的做法是让应用暴露一个只读接口返回规范内容再由 Swagger UI 之类的渲染器去读它。下面是程序化生成并直接输出的写法用到了OpenApi\Generator?php declare(strict_types1); // PHP 8.5 zircote/swagger-php namespace App\Http; use OpenApi\Generator; final class OpenApiEndpoint { /** 规范文件的缓存路径避免每个请求都重新扫描源码 */ private const CACHE __DIR__ . /../../var/openapi.json; public function __invoke(): void { $json $this-specJson(); header(Content-Type: application/json; charsetutf-8); header(Cache-Control: public, max-age300); echo $json; } private function specJson(): string { // 生产环境下读取缓存开发环境每次重新扫描改完代码立刻可见 if (is_file(self::CACHE) getenv(APP_ENV) prod) { $cached file_get_contents(self::CACHE); if ($cached ! false) { return $cached; } } $openapi Generator::scan([__DIR__ . /../Controller]); $json $openapi-toJson(); if (!is_dir(dirname(self::CACHE))) { mkdir(dirname(self::CACHE), 0775, true); } file_put_contents(self::CACHE, $json, LOCK_EX); return $json; } }把它挂到一个只读路由上例如GET /openapi.json即可。注意别把文档端点暴露到公网生产环境它会把你的全部内部接口结构、参数名、示例数据交代得清清楚楚。用 nginx 限制来源是最简单的保护方式location /openapi.json { allow 10.0.0.0/8; # 只允许内网与办公网段 deny all; fastcgi_pass 127.0.0.1:9000; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root/index.php; }如果只是想在本地顺手把规范整理一下再输出PHP 8.5 的管道操作符|PHP 8.5 引入能让这类小工具读起来更顺?php declare(strict_types1); // PHP 8.5 $normalize static fn (string $json): string json_encode( json_decode($json, true, 512, JSON_THROW_ON_ERROR), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); // 左侧的值会被当作右侧可调用的唯一参数传入 $pretty file_get_contents(openapi.json) | $normalize; file_put_contents(openapi.pretty.json, $pretty);管道操作符右侧要求是可调用的单参形式闭包、一等可调用语法等左侧的值作为唯一实参传入。用在读取 → 规范化 → 写回这种线性数据流上最自然。常见坑点1. 在 PHP 8.5 上装不动依赖❌ 看到does not satisfy that requirement就手动去改composer.json里的版本号或者降低 PHP 版本。 ✅ 先确认依赖库的最新版是否已支持 8.5确实没有时再用--ignore-platform-reqphp临时绕过并记一条待办等库更新后去掉这个参数。2. 扫描目录里混进了不该扫的文件❌Generator::scan([__DIR__])扫了整个项目把测试、迁移、缓存目录里的同名属性一起生成进去产出重复的OA\Info。 ✅ 只扫描控制器/请求类所在的目录用--exclude排除测试与vendor。3. 属性里用错误的嵌套写法❌#[OA\Response(response: 200, content: OA\JsonContent(...))]——嵌套对象必须new。 ✅content: new OA\JsonContent(...)。4. 生成结果为空❌ 命令行跑完openapi.json里只有openapi和info没有任何paths。 ✅ 检查三件事被扫描的文件里属性命名空间别名是否use OpenApi\Attributes as OA;类是否在命名空间下被正确加载命令行传入的路径是否为源码真实路径相对路径在不同工作目录下会扫不到东西。5. 每次请求都全量扫描源码❌ 文档端点直接调用Generator::scan()每个请求把整个src/遍历一遍。 ✅ 生产环境读缓存文件发版时重新生成开发环境才实时扫描。6. 把openapi.json提交进版本库后从不更新❌ 生成了规范文件提交一次此后再没重新生成过。 ✅ 把它并入 CI 检查或用发版脚本自动重新生成并提交。7. 示例数据里带真实业务信息❌example: usercompany.com用了真实客户邮箱或示例里塞了真实的订单号。 ✅ 统一用假数据并在 CI 里加一条简单的关键词检查。8. 把文档端点暴露在公网❌ 生产域名下GET /openapi.json任何人都能访问。 ✅ 用网关或 nginx 限制来源 IP或仅为内网环境注册该路由并加鉴权。总结环节做法关键点安装composer require --dev zircote/swagger-phpPHP 8.5 上先检查依赖的 PHP 版本约束描述接口在控制器上写#[OA\...]属性嵌套对象必须写newresponses是数组生成规范./vendor/bin/openapi --output openapi.json src/选项以当前版本的--help为准交付文档暴露只读端点 任意 OpenAPI 3.x 渲染器生产环境读缓存、限制访问来源防腐化生成命令写进 CI用git diff --exit-code检出差量在 PHP 8.5 上生成 API 文档语言层面几乎不需要额外操心属性语法从 8.0 起就稳定可用真正要花心思的是三件事依赖能否在 8.5 上安装、属性嵌套写法是否规范、以及文档生成是否被自动化。前两点决定你能不能跑起来最后一点决定这份文档三个月后还有没有人信。