CLAUDE.md · git:20261031.a21cbe0 · 2026-10-31 · sha256 c632c35c87013ba6
CLAUDE.md git:20261031.a21cbe0A
Immutable. This exact content never changes and is served at /api/v1/blob/c632c35c87013ba6.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 项目概述
`tangwei/apidocs` — 基于 Hyperf 的 Swagger/OpenAPI 3.x 文档自动生成组件。通过 PHP 8 Attributes 扫描控制器路由,在应用启动时生成 OpenAPI 描述文件,并内置多种文档 UI(Swagger UI、Knife4j、Redoc、RapiDoc、Scalar)及 llms.txt 输出。支持 Swoole / Swow / phar 部署。
## 常用命令
```bash
composer test # 运行全部测试(phpunit -c phpunit.xml)
vendor/bin/phpunit -c phpunit.xml --filter testMethodName tests/SwaggerPathsTest.php # 运行单个测试
composer analyse # PHPStan 静态分析(-l 0,仅 src/)
composer cs-fix # php-cs-fixer 格式化 src 和 tests
```
CI 矩阵覆盖 PHP 8.1/8.2/8.3/8.4 + Hyperf 3.1,以及 PHP 8.2/8.3/8.4 + Hyperf 3.2(pin 对应 `hyperf/di` + `tangwei/dto:dev-master`)。本包通过 `tangwei/dto ~4.0.0` 传递依赖 Hyperf 3.1/3.2。
## 架构核心
### 启动期生成流水线(理解本组件的关键)
OpenAPI 文件**不是请求时生成的**,而是在应用启动时由事件监听器驱动:
1. `BootAppRouteListener`(BootApplication 事件)— 在第一个 HTTP server 的路由上注册 `{prefix_url}` 路由组(UI 页面、`/webjars/*`、`{httpName}.json/yaml`、llms.txt 等),并把文档访问 URL 写入静态属性供 `AfterWorkerStartListener` 打印。
2. `AfterDtoStartListener`(`Hyperf\DTO\Event\AfterDtoStart` 事件,由 tangwei/dto 在扫描完路由后发出)— **每个 server 触发一次**:遍历该 server 的全部路由 Handler,对每个 `控制器@方法` 调 `SwaggerPaths::addPath()` 解析注解生成 `OA\PathItem`,最后 `SwaggerOpenApi::save()` 写入 `output_dir/{serverName}.json|yaml`。
3. `SwaggerOpenApi` 是**按 server 累积状态**的构建器:`init(serverName)` 重置 → 各 Generate 类向其 SplPriorityQueue(paths/tags 按 position 排序)投递 → `save()` 落盘 → `clean()` 释放。多 server 应用会为每个 server 各生成一份文件。
**注意**:`dtoConfig->isScanCacheable()` 为 true 时 `AfterDtoStartListener` 跳过生成(第 56-58 行提前 return)——扫描缓存模式下运行环境可能没有 output_dir 中的文件。
### 注解 → OpenAPI 的转换链
- `SwaggerPaths::addPath()` 读取类/方法注解(`#[Api]`、`#[ApiOperation]`、`#[ApiHeader]`、`#[ApiResponse]`、`#[ApiFormData]`、`#[ApiSecurity]`),委托给:
- `GenerateParameters` — 从方法签名 + DTO 类生成 parameters/requestBody
- `GenerateResponses` — 从方法**返回类型**(`MethodDefinitionCollector`)+ `#[ApiResponse]` + 全局 `GlobalResponse` 配置生成 responses;控制器方法返回具体类才能获得准确文档
- `SwaggerComponents` — DTO 类的 `#[ApiModelProperty]`/验证注解 → `components.schemas`,继承自 tangwei/dto 的 `PropertyManager`
- `SwaggerConfig` 用 JsonMapper(`bIgnoreVisibility`)把 `config/autoload/api_docs.php` 直接映射到私有属性——**配置键名必须与属性名一致**(snake_case),新增配置项 = 新增同名私有属性。
### ApiVariable 代理类机制
`#[ApiVariable]` 标记的 DTO 属性(类型在运行时才能确定的"可变类型")由 `GenerateProxyClass` 在运行时通过 PHP-Parser 重写原类 AST(`Ast\ResponseVisitor` 替换属性类型和命名空间为 `ApiDocs\Proxy`),写入 `proxy_dir`(默认 `runtime/container/proxy/`)供 schema 生成使用。
### 文件服务端点
`SwaggerController`(json/yaml/md/静态文件)和 `SwaggerUiController`(各 UI 页面 + knife4j webjars)按请求实例化。静态资源路径硬编码指向 `vendor/tangwei/swagger-ui/dist` 和 `vendor/tangwei/knife4j-ui/dist`(knife4j-ui 是 suggest 依赖,未安装时相关路由会 500)。三类端点校验方式不同:`getFile` 用 scandir 白名单精确匹配,`knife4jFile` 用 sanitize + realpath 前缀校验(嵌套路径无法白名单)。`fileResponse` 在 Swoole 下用 `SwooleFileStream`(sendfile),Swow/phar 下退回 `file_get_contents`。
### 与 tangwei/dto 的关系
本组件重度依赖 `tangwei/dto`(`Hyperf\DTO\*`):注解扫描(`ApiAnnotation::classMetadata`)、DTO 验证、属性管理、Mapper 均来自该包。修改扫描/注解相关行为时,先确认逻辑在本包还是 dto 包。
## 测试约定
- 测试基类 `SwaggerUiControllerTestable` 重写了构造函数且**不调 `parent::__construct`**——父类构造函数的逻辑(目录检查、scandir)在测试中不会被覆盖到。
- `tests/Request/` 下的 DTO 是多个测试共用的 fixture。
- CI 在 hyperf/hyperf 容器镜像中运行,本地无 Swoole 也可跑 phpunit(测试不依赖 server 启动)。
## 示例与文档
- `example/` 目录是注解用法的活文档(各参数注解、分页、枚举、递归类型的完整示例),改注解行为时对照它验证。
- README.md / README_EN.md 需保持同步;环境要求以 composer.json 为准(README 中的版本号容易滞后)。
- `publish/skills/api-docs/SKILL.md` 是通过 `vendor:publish --id=skill` 发布给使用方项目的 AI 使用指南,修改注解签名或配置键时需同步更新。