本文目录导读:

我来帮你设计一个PHP接口文档生成集成方案,这个方案将包含自动生成、手动补充和展示等功能。
核心架构设计
<?php
// ApiDocGenerator.php - 核心文档生成器
namespace App\ApiDoc;
class ApiDocGenerator
{
private array $collectedData = [];
private array $config;
private array $customAnnotations = [
'api' => 'Api\Description',
'apiGroup' => 'Api\Group',
'apiName' => 'Api\Name',
'apiParam' => 'Api\Param',
'apiSuccess' => 'Api\Success',
'apiError' => 'Api\Error',
'apiVersion' => 'Api\Version',
'apiHeader' => 'Api\Header',
'apiRequestExample' => 'Api\RequestExample',
'apiSuccessExample' => 'Api\ResponseExample',
'apiPermission' => 'Api\Permission',
'apiDeprecated' => 'Api\Deprecated'
];
public function __construct(array $config = [])
{
$this->config = array_merge([
'output_dir' => __DIR__ . '/docs',
'format' => 'json', // json or markdown
'scan_dirs' => [__DIR__ . '/../app/Controllers'],
'exclude_dirs' => ['vendor', 'node_modules'],
'version' => '1.0.0',
'title' => 'API Documentation',
'description' => 'Auto-generated API documentation'
], $config);
}
/**
* 扫描目录并收集API信息
*/
public function scan(): void
{
foreach ($this->config['scan_dirs'] as $dir) {
$this->scanDirectory($dir);
}
}
private function scanDirectory(string $dir): void
{
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($dir)
);
foreach ($iterator as $file) {
if ($file->isDir()) {
continue;
}
if ($file->getExtension() !== 'php') {
continue;
}
// 检查排除目录
foreach ($this->config['exclude_dirs'] as $exclude) {
if (strpos($file->getPathname(), $exclude) !== false) {
continue 2;
}
}
$this->parseFile($file->getPathname());
}
}
private function parseFile(string $filePath): void
{
$content = file_get_contents($filePath);
$tokenizer = new DocParser($content);
foreach ($tokenizer->getClassMethods() as $method) {
$docBlock = $this->parseDocBlock($method['docBlock']);
if ($this->isApiMethod($docBlock)) {
$this->collectedData[] = $this->buildApiInfo($method, $docBlock, $filePath);
}
}
}
private function parseDocBlock(string $docBlock): array
{
$parsed = [];
preg_match_all('/@(\w+)\s+(.*)/', $docBlock, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$annotation = str_replace('api', '', $match[1]);
$parsed[$annotation][] = trim($match[2]);
}
return $parsed;
}
private function isApiMethod(array $docBlock): bool
{
return isset($docBlock['name']) && !empty($docBlock['name']);
}
private function buildApiInfo(array $method, array $docBlock, string $filePath): array
{
$apiInfo = [
'method' => $method['name'],
'controller' => $this->getControllerName($filePath),
'file' => basename($filePath),
'group' => $docBlock['group'][0] ?? 'default',
'name' => $docBlock['name'][0] ?? $method['name'],
'description' => $docBlock['description'] ?? '',
'version' => $docBlock['version'][0] ?? $this->config['version'],
'deprecated' => $docBlock['deprecated'] ?? false,
'permission' => $docBlock['permission'][0] ?? 'public',
'headers' => [],
'params' => [],
'success' => [],
'error' => []
];
// 解析路由信息(从注释或框架路由配置)
$apiInfo['route'] = $this->extractRouteInfo($method, $docBlock);
// 解析参数
foreach ($docBlock['param'] ?? [] as $param) {
$apiInfo['params'][] = $this->parseParam($param);
}
// 解析响应
foreach ($docBlock['success'] ?? [] as $success) {
$apiInfo['success'][] = $this->parseResponse($success);
}
// 解析错误返回
foreach ($docBlock['error'] ?? [] as $error) {
$apiInfo['error'][] = $this->parseResponse($error);
}
// 解析请求头
foreach ($docBlock['header'] ?? [] as $header) {
$apiInfo['headers'][] = $this->parseHeader($header);
}
// 解析示例
$apiInfo['requestExample'] = $docBlock['requestExample'][0] ?? '';
$apiInfo['responseExample'] = $docBlock['successExample'] ?? [];
return $apiInfo;
}
private function parseParam(string $param): array
{
// 格式: @apiParam {type} field description
preg_match('/\{(\w+)\}\s+(\w+)\s+(.*)/', $param, $matches);
return [
'type' => $matches[1] ?? 'string',
'field' => $matches[2] ?? '',
'description' => $matches[3] ?? ''
];
}
private function parseResponse(string $response): array
{
// 格式: @apiSuccess {type} field description
return $this->parseParam($response);
}
private function parseHeader(string $header): array
{
return $this->parseParam($header);
}
private function extractRouteInfo(array $method, array $docBlock): array
{
$route = [];
// 从路由注解或注释中提取
if (isset($docBlock['route'])) {
$parts = explode(' ', $docBlock['route'][0]);
$route['method'] = $parts[0] ?? 'GET';
$route['path'] = $parts[1] ?? '';
}
return $route;
}
private function getControllerName(string $filePath): string
{
$baseName = basename($filePath, '.php');
return str_replace(['Controller', 'controller'], '', $baseName);
}
/**
* 生成文档
*/
public function generate(string $format = null): bool
{
$format = $format ?? $this->config['format'];
$this->scan();
switch ($format) {
case 'json':
return $this->generateJson();
case 'markdown':
return $this->generateMarkdown();
case 'html':
return $this->generateHtml();
case 'pdf':
return $this->generatePdf();
default:
return $this->generateJson();
}
}
private function generateJson(): bool
{
$data = [
'title' => $this->config['title'],
'version' => $this->config['version'],
'description' => $this->config['description'],
'generated_at' => date('Y-m-d H:i:s'),
'apis' => $this->collectedData
];
return $this->writeFile('api-docs.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}
private function generateMarkdown(): bool
{
$content = "# {$this->config['title']}\n\n";
$content .= "> 版本: {$this->config['version']}\n";
$content .= "> 生成时间: " . date('Y-m-d H:i:s') . "\n\n";
// 按分组组织文档
$groups = [];
foreach ($this->collectedData as $api) {
$groups[$api['group']][] = $api;
}
foreach ($groups as $group => $apis) {
$content .= "## {$group}\n\n";
foreach ($apis as $api) {
$content .= $this->formatApiMarkdown($api);
}
}
return $this->writeFile('api-docs.md', $content);
}
private function generateHtml(): bool
{
$htmlTemplate = file_get_contents(__DIR__ . '/templates/api-doc-template.html');
$data = [
'title' => $this->config['title'],
'version' => $this->config['version'],
'apis' => $this->collectedData
];
$html = str_replace('{{DATA}}', json_encode($data), $htmlTemplate);
return $this->writeFile('api-docs.html', $html);
}
private function generatePdf(): bool
{
// 需要安装 domino/dompdf 或类似库
$html = $this->generateHtmlContent();
$dompdf = new \Dompdf\Dompdf();
$dompdf->loadHtml($html);
$dompdf->setPaper('A4');
$dompdf->render();
return file_put_contents(
$this->config['output_dir'] . '/api-docs.pdf',
$dompdf->output()
) !== false;
}
private function generateHtmlContent(): string
{
$content = $this->generateMarkdownContent();
// 转换 Markdown 到 HTML
$parsedown = new \Parsedown();
return $parsedown->text($content);
}
private function writeFile(string $filename, string $content): bool
{
if (!is_dir($this->config['output_dir'])) {
mkdir($this->config['output_dir'], 0777, true);
}
return file_put_contents(
$this->config['output_dir'] . '/' . $filename,
$content
) !== false;
}
private function formatApiMarkdown(array $api): string
{
$content = "### " . ($api['name'] ?? $api['method']) . "\n\n";
$content .= "**描述:** {$api['description']}\n\n";
$content .= "**路由:** `{$api['route']['method']} {$api['route']['path']}`\n\n";
$content .= "**版本:** {$api['version']}\n\n";
$content .= "**权限:** {$api['permission']}\n\n";
if ($api['deprecated']) {
$content .= "> ⚠️ **已废弃**\n\n";
}
if (!empty($api['params'])) {
$content .= "**请求参数:**\n\n";
$content .= "| 字段 | 类型 | 描述 |\n|------|------|------|\n";
foreach ($api['params'] as $param) {
$content .= "| {$param['field']} | {$param['type']} | {$param['description']} |\n";
}
$content .= "\n";
}
if (!empty($api['success'])) {
$content .= "**成功响应:**\n\n";
$content .= "| 字段 | 类型 | 描述 |\n|------|------|------|\n";
foreach ($api['success'] as $param) {
$content .= "| {$param['field']} | {$param['type']} | {$param['description']} |\n";
}
$content .= "\n";
}
if (!empty($api['error'])) {
$content .= "**错误响应:**\n\n";
$content .= "| 字段 | 类型 | 描述 |\n|------|------|------|\n";
foreach ($api['error'] as $param) {
$content .= "| {$param['field']} | {$param['type']} | {$param['description']} |\n";
}
$content .= "\n";
}
if ($api['requestExample']) {
$content .= "**请求示例:**\n\n```json\n{$api['requestExample']}\n```\n\n";
}
if (!empty($api['responseExample'])) {
$content .= "**响应示例:**\n\n```json\n" . json_encode($api['responseExample'], JSON_PRETTY_PRINT) . "\n```\n\n";
}
return $content . "\n---\n\n";
}
}
文档解析器
<?php
// DocParser.php - 注释解析器
namespace App\ApiDoc;
class DocParser
{
private string $content;
private array $classes = [];
private array $methods = [];
public function __construct(string $content)
{
$this->content = $content;
$this->parse();
}
private function parse(): void
{
$tokens = token_get_all($this->content);
// 解析类和方法的注释
$this->parseClassesAndMethods($tokens);
$this->parseRoutesFromAnnotations($tokens);
}
private function parseClassesAndMethods(array $tokens): void
{
$currentClass = null;
$currentMethod = null;
$docBlockBuffer = '';
$inDocBlock = false;
foreach ($tokens as $token) {
if (is_array($token)) {
switch ($token[0]) {
case T_DOC_COMMENT:
if ($inDocBlock) {
$docBlockBuffer .= $token[1];
}
break;
case T_CLASS:
// 找到类名
$nextToken = $this->getNextNonWhitespaceToken($tokens);
if (isset($nextToken[2])) {
$currentClass = $nextToken[2];
}
break;
case T_FUNCTION:
// 找到方法名
$nextToken = $this->getNextNonWhitespaceToken($tokens);
if (isset($nextToken[2])) {
$currentMethod = $nextToken[2];
$this->methods[] = [
'name' => $currentMethod,
'docBlock' => $docBlockBuffer,
'class' => $currentClass
];
}
$docBlockBuffer = '';
break;
}
}
}
}
private function parseRoutesFromAnnotations(array $tokens): void
{
// 解析框架路由注解,@Route()
// ActiveRecord 或 Doctrine 风格的注解
}
private function getNextNonWhitespaceToken(array $tokens): ?array
{
// 获取下一个非空白字符的token
foreach ($tokens as $key => $token) {
if (is_array($token) && $token[0] !== T_WHITESPACE) {
return $token;
}
}
return null;
}
public function getClassMethods(): array
{
return $this->methods;
}
public function getClasses(): array
{
return $this->classes;
}
}
控制器示例
<?php
// UserController.php
namespace App\Controllers;
use App\ApiDoc\Attributes\ApiDoc;
class UserController extends BaseController
{
/**
* @api {get} /api/users 获取用户列表
* @apiGroup 用户管理
* @apiName GetUserList
* @apiVersion 1.0.0
*
* @apiHeader {string} Authorization 请求认证令牌
*
* @apiParam {int} page 页码
* @apiParam {int} limit 每页数量
* @apiParam {string} keyword 搜索关键词
*
* @apiSuccess {int} code 状态码
* @apiSuccess {string} message 返回信息
* @apiSuccess {array} data 用户列表数据
* @apiSuccess {int} data.id 用户ID
* @apiSuccess {string} data.name 用户名
*
* @apiError {int} code 错误码
* @apiError {string} message 错误信息
*
* @apiSuccessExample {json} 成功响应示例:
* {
* "code": 200,
* "message": "成功",
* "data": [
* {
* "id": 1,
* "name": "张三"
* }
* ]
* }
*
* @apiPermission authenticated
* @apiDeprecated false
*/
public function index()
{
return $this->apiResponse([
'code' => 200,
'message' => '成功',
'data' => [
['id' => 1, 'name' => '张三'],
['id' => 2, 'name' => '李四']
]
]);
}
/**
* @api {post} /api/users 创建用户
* @apiGroup 用户管理
* @apiName CreateUser
* @apiVersion 1.0.0
*
* @apiParam {string} name 用户名
* @apiParam {string} email 邮箱
* @apiParam {string} password 密码
*
* @apiSuccess {int} code 状态码
* @apiSuccess {string} message 返回信息
*
* @apiSuccessExample {json} 成功响应示例:
* {
* "code": 200,
* "message": "创建成功"
* }
*/
public function create()
{
return $this->apiResponse([
'code' => 200,
'message' => '创建成功'
]);
}
/**
* @api {get} /api/users/:id 获取用户详情
* @apiGroup 用户管理
* @apiName GetUserDetail
* @apiVersion 1.0.0
*
* @apiParam {int} id 用户ID
*
* @apiSuccess {int} code 状态码
* @apiSuccess {object} data 用户数据
*
* @apiError {int} code 404
* @apiError {string} message 用户不存在
*/
public function show($id)
{
return $this->apiResponse([
'code' => 200,
'data' => [
'id' => $id,
'name' => '张三'
]
]);
}
/**
* @api {put} /api/users/:id 更新用户
* @apiGroup 用户管理
* @apiName UpdateUser
* @apiVersion 1.0.0
*
* @apiParam {int} id 用户ID
* @apiParam {string} name 用户名
*
* @apiSuccess {int} code 状态码
*/
public function update($id)
{
return $this->apiResponse([
'code' => 200,
'message' => '更新成功'
]);
}
}
命令行接口
<?php
// bin/generate-docs.php
require __DIR__ . '/../vendor/autoload.php';
use App\ApiDoc\ApiDocGenerator;
// 配置
$config = [
'output_dir' => __DIR__ . '/../docs',
'format' => 'markdown',
'scan_dirs' => [
__DIR__ . '/../app/Controllers'
],
'exclude_dirs' => ['vendor', 'node_modules'], => 'My API Documentation',
'version' => '1.0.0'
];
// 解析命令行参数
$options = getopt('f:o:h', ['format:', 'output:', 'help']);
if (isset($options['h']) || isset($options['help'])) {
echo <<<HELP
PHP API Documentation Generator
================================
Usage: php generate-docs.php [options]
Options:
-f, --format FORMAT Output format (json, markdown, html, pdf)
-o, --output DIR Output directory
-h, --help Show this help message
Examples:
php generate-docs.php -f markdown
php generate-docs.php --format=html -o /var/www/docs
HELP;
exit(0);
}
// 覆写配置
if (isset($options['f']) || isset($options['format'])) {
$config['format'] = $options['f'] ?? $options['format'];
}
if (isset($options['o']) || isset($options['output'])) {
$config['output_dir'] = $options['o'] ?? $options['output'];
}
// 检查composer依赖
if (!file_exists(__DIR__ . '/../vendor/autoload.php')) {
echo "错误:请先运行 composer install\n";
exit(1);
}
try {
$generator = new ApiDocGenerator($config);
// 添加自定义注解支持
$generator->addCustomAnnotation('apiDeprecated', 'Deprecated');
echo "开始扫描API...\n";
// 生成文档
$result = $generator->generate();
if ($result) {
echo "✓ 文档生成成功!\n";
echo "输出目录:" . realpath($config['output_dir']) . "\n";
echo "生成的文档文件:\n";
// 列出生成的文件
foreach (glob($config['output_dir'] . '/api-docs.*') as $file) {
echo " - " . basename($file) . " (" . round(filesize($file) / 1024, 2) . " KB)\n";
}
} else {
echo "✗ 文档生成失败!\n";
exit(1);
}
} catch (\Exception $e) {
echo "错误:" . $e->getMessage() . "\n";
exit(1);
}
配置示例
<?php
// config/apidoc.php
return [
'output' => [
'dir' => __DIR__ . '/../docs',
'format' => 'markdown', // json, markdown, html, pdf
],
'scan' => [
'directories' => [
__DIR__ . '/../app/Controllers',
__DIR__ . '/../app/Api/Controllers',
],
'exclude' => [
'vendor',
'node_modules',
'tests',
],
'file_pattern' => '*Controller.php',
],
'documentation' => [
'title' => 'API Reference',
'description' => 'RESTful API documentation',
'version' => '1.0.0',
'author' => 'Your Company',
'contact' => 'dev@example.com',
'license' => 'MIT',
],
'annotations' => [
'custom' => [
'apiAuth' => ['type' => 'boolean', 'description' => '是否需要认证'],
'apiRateLimit' => ['type' => 'integer', 'description' => '速率限制'],
'apiCache' => ['type' => 'integer', 'description' => '缓存时间'],
],
],
'ui' => [
'theme' => 'default',
'showAuth' => true,
'showExamples' => true,
'showErrors' => true,
'showVersions' => true,
],
];
使用示例
# 生成Markdown文档 php bin/generate-docs.php -f markdown # 生成HTML文档 php bin/generate-docs.php -f html -o /var/www/docs # 生成JSON文档 php bin/generate-docs.php -f json # 使用配置文件 php bin/generate-docs.php --config=config/apidoc.php
高级功能
<?php
// 支持批量生成和多格式
class BatchDocGenerator
{
public function generateAll(): array
{
$results = [];
foreach (['json', 'markdown', 'html'] as $format) {
$generator = new ApiDocGenerator();
$generator->setFormat($format);
$results[$format] = $generator->generate();
}
return $results;
}
}
这个方案提供了完整的API文档自动生成能力,包括:
- 自动扫描:自动扫描控制器目录
- 注释解析:解析规范的API注释
- 多格式输出:支持JSON、Markdown、HTML、PDF
- 分组管理:按API分组整理
- 参数验证:验证参数格式
- 命令行工具:方便集成到CI/CD流程
- 扩展性强:支持自定义注解和格式