ThinkPHP 使用 SwaggerUi 自动生成 api 文档
1、下载swagger-ui
GitHub地址:https://github.com/swagger-api/swagger-ui
2、修改 ThinkPHP 的 build.php ,执行命令生成需要创建的模块
// 定义api模块的自动生成 (按照实际定义的文件名生成) 'api' => [ '__file__' => ['common.php'], '__dir__' => ['controller'], 'controller' => ['Passport'], 'model' => [], 'view' => [], ],
命令: php think build
3、修改刚刚生成的模块下的 config.php 文件
return [ 'default_return_type' => 'json', // 修改控制器默认输出json对象 'url_param_type' => 1, // URL参数方式 0 按名称成对解析 1 按顺序解析 ];
4、在刚刚生成的 application/api/contorller/Passport.php 下添加这几个方法
<?php namespace app\api\controller; /** * swagger: 登录相关 */ class Passport { /** * post: 发送验证码 * path: sendVerify/{phone}/{deviceType} * method: sendVerify * param: phone - {string} 手机号 * param: deviceType - {int} = [0|1|2|3|4] 设备类型(0: android手机, 1: ios手机, 2: android平板, 3: ios平板, 4: pc) */ public function sendVerify($phone, $deviceType) { return [ 'code' => 200, 'message' => '发送验证码', 'data' => [ 'phone' => $phone, 'deviceType' => $deviceType ] ]; } /** * post: 登陆 * path: login * method: login * param: phone - {string} 手机号 * param: password - {string} 密码 * param: deviceType - {int} = [0|1|2|3|4] 设备类型(0: android手机, 1: ios手机, 2: android平板, 3: ios平板, 4: pc) * param: verifyCode - {string} = 0 验证码 */ public function login($phone, $password, $deviceType, $verifyCode = '0') { return [ 'code' => 200, 'message' => '登陆成功', 'data' => [ 'phone' => $phone, 'password' => $password, 'deviceType' => $deviceType, 'verifyCode' => $verifyCode ] ]; } /** * get: 获取配置 * path: profile * method: profile * param: keys - {string[]} 需要获取配置的Key值数组 */ public function profile($keys) { return [ 'code' => 200, 'message' => '获取成功', 'data' => $keys ]; } }
5、把 swagger-ui 目录下的 dist 放到你的 public 目录下,然后更名为 swagger。
6、修改 swagger 下 index.html 的路径:
window.onload = function() { // Begin Swagger UI call region const ui = SwaggerUIBundle({ url : window.location.href.replace(window.location.hash, "").replace(/[^/]+$/, "swagger.json"), dom_id: '#swagger-ui', deepLinking: true, presets: [ SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset ], plugins: [ SwaggerUIBundle.plugins.DownloadUrl ], layout: "StandaloneLayout" }) // End Swagger UI call region window.ui = ui }
7、整合ThinkPHP 和 swaggerUI:
在 public 目录下新建 api.php
<?php // +---------------------------------------------------------------------- // | ThinkPHP SWAGGER [ 够用就好 ] // +---------------------------------------------------------------------- // | Copyright (c) 2016 http://jitlee.com All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://jitlee.com/licenses/LICENSE-1.0 ) // +---------------------------------------------------------------------- // | Author: Jitlee.Wan <www.wpj@163.com> // +---------------------------------------------------------------------- // 定义应用目录 define('APP_PATH', __DIR__ . '/../application'); $tags = array(); // Tags对象 $paths = array(); // Path数组 $module_dir = opendir(APP_PATH); while (($module_name = readdir($module_dir)) !== false) { if ($module_name != 'api') continue; //可以自己设置需要哪些模块用作api输出页面 $module_path = APP_PATH . DIRECTORY_SEPARATOR . $module_name; //构建子目录路径 if (is_dir($module_path)) { $module = strtolower($module_name); $module_child_dir = opendir($module_path); while (($module_child_name = readdir($module_child_dir)) !== false) { $module_child_path = $module_path . DIRECTORY_SEPARATOR . $module_child_name; //构建子目录路径 if (is_dir($module_child_path) && $module_child_name == 'controller') { $controller_dir = opendir($module_child_path); while (($controller_file = readdir($controller_dir)) !== false) { $controller_path = $module_child_path . DIRECTORY_SEPARATOR . $controller_file; //构建子目录路径 $controller_name = strtolower(basename($controller_path, '.php')); $contents = file_get_contents($controller_path); if (preg_match_all('/swagger:\s*([^\n]+)/i', $contents, $swagger_matches)) { // 添加tag $found_tag = false; foreach ($tags as $tag) { if ($tag['name'] == $controller_name) { $found_tag = true; break; } } if (!$found_tag) { array_push($tags, array( 'name' => $controller_name, 'description' => $swagger_matches[1][0] )); } // 添加path if (preg_match_all('/\/\*((?!\*\/).)+\*\//s', $contents, $func_matches)) { $length = count($func_matches[0]); if ($length > 1) { for ($i = 1; $i < $length; $i++) { $func_array = array(); // 解析每个方法 $func_contents = $func_matches[0][$i]; // 方法说明 if (!preg_match_all('/(get|post|delete)\s*:\s*([^\n]+)/i', $func_contents, $matches)) { break; } $method = $matches[1][0]; $summary = $matches[2][0]; // 路径 if (!preg_match_all('/path\s*:\s*([^\n]+)/i', $func_contents, $matches)) { break; } $path = $matches[1][0]; // 方法名称 $operations = explode('/', $path); $operationId = $operations[0]; if (preg_match_all('/method\s*:\s*([^\n]+)/i', $func_contents, $matches)) { $operationId = $matches[1][0]; } $paths[$path] = array(); $parameters = array(); $func = array( 'tags' => [$controller_name], 'summary' => $summary, 'description' => '', 'operationId' => $operationId, 'produces' => ['application/json'] ); // 参数 $pattern = '/param\s*:\s*(?<name>\w+)\s*-\s*\{(?<type>\w+(?<array>\[\])?)\}\s*(=\s*((\[(?<enum>[^]]+)\])|(?<default>[^\s]+))\s*)?(?<summary>[^*]+)/i'; if (preg_match_all($pattern, $func_contents, $matches)) { $names = $matches['name']; // 参数名称 $types = $matches['type']; // 参数类型 $enums = $matches['enum']; // 参数枚举 $defaults = $matches['default']; // 默认值 $summarys = $matches['summary']; // 参数说明 $arrays = $matches['array']; // 参数说明 $params_count = count($names); for ($j = 0; $j < $params_count; $j++) { $in = $method == 'get' ? 'query' : 'formData'; if (strpos($path, '{' . $names[$j] . '}') !== false) { $in = 'path'; } $parameter = array( 'name' => $names[$j], 'in' => $in, 'required' => true, 'description' => $summarys[$j] ); if ($defaults[$j] !== '') { $parameter['required'] = false; $parameter['defaultValue'] = $defaults[$j]; } $type = str_replace('[]', '', $types[$j]); if ($type == 'int') { $type = 'integer'; } if ($arrays[$j] != '') { // 是否数据参数 $parameter['type'] = 'array'; $parameter['items'] = array( 'type' => str_replace('[]', '', $type) ); $parameter['collectionFormat'] = 'brackets'; // url带中括号 // $parameter['collectionFormat'] = 'multi'; // url不带中括号 } else if ($enums[$j] != '') { // 是否枚举参数 $enum = explode('|', $enums[$j]); $parameter['type'] = $type; $parameter['enum'] = $enum; } else { $parameter['type'] = $type; } array_push($parameters, $parameter); } } $func['parameters'] = $parameters; // 生成api访问路径 $paths['/' . $module . '/' . $controller_name . '/' . $path][$method] = $func; } } } } } closedir($controller_dir); } } closedir($module_child_dir); } } closedir($module_dir); $swagger = array( 'swagger' => '2.0', 'info' => array( 'description' => 'APP 后台服务', 'version' => '1.0.0', 'title' => '[我的APP]Swagger', 'termsOfService' => 'http://www.ritacc.cn/', 'contact' => array( 'email' => 'www.wpj@163.com' ), 'license' => array( 'name' => 'Apache 2.0', 'url' => 'http://www.apache.org/licenses/LICENSE-2.0.html' ) ), 'host' => $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'], 'basePath' => '', 'tags' => $tags, 'schemes' => [ 'http' ], 'paths' => $paths, 'securityDefinitions' => array(), 'definitions' => array(), 'externalDocs' => array( 'description' => 'Find out more about Swagger', 'url' => 'http://swagger.io' ) ); $jsonFile = fopen("swagger/swagger.json", "w") or die("Unable to open file!"); fwrite($jsonFile, json_encode($swagger)); fclose($jsonFile); // 跳转到Swagger UI $url = '/swagger/index.html'; Header('HTTP/1.1 303 See Other'); Header("Location: $url"); exit;
8、打开浏览器运行 public 下的 api.php 就 ok 了。