This commit is contained in:
wangjinlei
2022-04-06 18:02:49 +08:00
parent e34f87de36
commit c1885928ff
262 changed files with 18633 additions and 0 deletions

126
vendor/itxq/api-doc-php/src/ApiDoc.php vendored Normal file
View File

@@ -0,0 +1,126 @@
<?php
/**
* ==================================================================
* 文 件 名: ApiDoc.php
* 概 要: ApiDoc生成
* 作 者: IT小强
* 创建时间: 2018/6/5 9:40
* 修改时间:
* copyright (c) 2016 - 2018 mail@xqitw.cn
* ==================================================================
*/
namespace itxq\apidoc;
use itxq\apidoc\lib\ParseComment;
/**
* ApiDoc生成
* Class ApiDoc
* @package itxq\apidoc
*/
class ApiDoc
{
/**
* @var array - 结构化的数组
*/
private $ApiTree = [];
/**
* @var array - 要生成API的Class类名
*/
private $class = [];
/**
* @var array - 忽略生成的类方法名
*/
private $filterMethod = ['__construct'];
/**
* ApiDoc 构造函数.
* @param array $config - 配置信息
*/
public function __construct($config) {
// 需要解析的类
if (isset($config['class'])) {
$this->class = array_merge($this->class, $config['class']);
}
// 忽略生成的类方法
if (isset($config['filter_method'])) {
$this->filterMethod = array_merge($this->filterMethod, $config['filter_method']);
}
}
/**
* 获取API文档数据
* @param int $type - 方法过滤,默认只获取 public类型 方法
* ReflectionMethod::IS_STATIC
* ReflectionMethod::IS_PUBLIC
* ReflectionMethod::IS_PROTECTED
* ReflectionMethod::IS_PRIVATE
* ReflectionMethod::IS_ABSTRACT
* ReflectionMethod::IS_FINAL
* @return array
*/
public function getApiDoc($type = \ReflectionMethod::IS_PUBLIC) {
foreach ($this->class as $classItem) {
$actionInfo = $this->_getActionComment($classItem, $type);
if (count($actionInfo) >= 1) {
$this->ApiTree[$classItem] = $this->_getClassComment($classItem);
$this->ApiTree[$classItem]['action'] = $actionInfo;
}
}
return $this->ApiTree;
}
/**
* 获取类的注释
* @param $class - 类名称(存在命名空间时要完整写入) eg: $class = 'itxq\\apidoc\\ApiDoc';
* @return array - 返回格式为数组(未获取到注释时返回空数组)
*/
private function _getClassComment($class) {
try {
$reflection = new \ReflectionClass($class);
$classDocComment = $reflection->getDocComment();
} catch (\Exception $exception) {
return [];
}
$parse = new ParseComment();
return $parse->parseCommentToArray($classDocComment);
}
/**
* 获取指定类下方法的注释
* @param $class - 类名称(存在命名空间时要完整写入) eg: $class = 'itxq\\apidoc\\ApiDoc';
* @param int $type - 方法过滤,默认只获取 public类型 方法
* ReflectionMethod::IS_STATIC
* ReflectionMethod::IS_PUBLIC
* ReflectionMethod::IS_PROTECTED
* ReflectionMethod::IS_PRIVATE
* ReflectionMethod::IS_ABSTRACT
* ReflectionMethod::IS_FINAL
* @return array - 返回格式为数组(未获取到注释时返回空数组)
*/
private function _getActionComment($class, $type = \ReflectionMethod::IS_PUBLIC) {
try {
$reflection = new \ReflectionClass($class);
//只允许生成public方法
$method = $reflection->getMethods($type);
} catch (\Exception $exception) {
return [];
}
$comments = [];
foreach ($method as $action) {
try {
$parse = new ParseComment();
$actionComments = $parse->parseCommentToArray($action->getDocComment());
if (count($actionComments) >= 1 && !in_array($action->name, $this->filterMethod)) {
$comments[$action->name] = $actionComments;
}
} catch (\Exception $exception) {
continue;
}
}
return $comments;
}
}

View File

@@ -0,0 +1,357 @@
<?php
/**
* ==================================================================
* 文 件 名: BootstrapApiDoc.php
* 概 要: BootstrapAPI文档生成
* 作 者: IT小强
* 创建时间: 2018/6/6 13:57
* 修改时间:
* copyright (c) 2016 - 2018 mail@xqitw.cn
* ==================================================================
*/
namespace itxq\apidoc;
use itxq\apidoc\lib\Tools;
/**
* BootstrapAPI文档生成
* Class BootstrapApiDoc
* @package itxq\apidoc
*/
class BootstrapApiDoc extends ApiDoc
{
/**
* @var string - Bootstrap CSS文件路径
*/
private $bootstrapCss = __DIR__ . '/../assets/css/bootstrap.min.css';
/**
* @var string - Bootstrap JS文件路径
*/
private $bootstrapJs = __DIR__ . '/../assets/js/bootstrap.min.js';
/**
* @var string - jQuery Js文件路径
*/
private $jQueryJs = __DIR__ . '/../assets/js/jquery.min.js';
/**
* @var string - 自定义CSS
*/
private $customCss = '<style type="text/css">
::-webkit-scrollbar {width: 5px;}
.navbar-collapse.collapse.show::-webkit-scrollbar {width: 0; height: 0;background-color: rgba(255, 255, 255, 0);}
::-webkit-scrollbar-track {background-color: rgba(255, 255, 255, 0.2);-webkit-border-radius: 2em;-moz-border-radius: 2em;border-radius: 2em;}
::-webkit-scrollbar-thumb {background-color: rgba(0, 0, 0, 0.8);-webkit-border-radius: 2em;-moz-border-radius: 2em;border-radius: 2em;}
::-webkit-scrollbar-button {-webkit-border-radius: 2em;-moz-border-radius: 2em;border-radius: 2em;height: 0;background-color: rgba(0, 0, 0, 0.9);}
::-webkit-scrollbar-corner {background-color: rgba(0, 0, 0, 0.9);}
#list-tab-left-nav{display: none;}
.doc-content{margin-top: 75px;}
.class-item .class-title {text-indent: 0.6em;border-left: 5px solid lightseagreen;font-size: 24px;margin: 15px 0;}
.action-item .action-title {text-indent: 0.6em;border-left: 3px solid #F0AD4E;font-size: 20px;margin: 8px 0;}
.table-item {background-color:#FFFFFF;padding-top: 10px;margin-bottom:10px;border: solid 1px #ccc;border-radius: 5px;}
.list-group-item-sub{padding: .5rem 1.25rem;}
.copyright-content{margin: 10px 0;}
</style>';
/**
* @var string - 自定义JS
*/
private $customJs = '<script type="text/javascript">
$(\'a[href*="#"]:not([href="#"])\').click(function() {
if (location.pathname.replace(/^\//, \'\') == this.pathname.replace(/^\//, \'\') && location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $("[name=\' + this.hash.slice(1) +\']");
if (target.length) {
var topOffset = target.offset().top - 60;
$("html, body").animate({
scrollTop: topOffset
}, 800);
return false;
}
}
});
</script>';
/**
* Bootstrap 构造函数.
* @param array $config - 配置信息
*/
public function __construct($config) {
parent::__construct($config);
// bootstrapJs文件路径
$this->bootstrapJs = Tools::getSubValue('bootstrap_js', $config, $this->bootstrapJs);
// jQueryJs文件路径
$this->jQueryJs = Tools::getSubValue('jquery_js', $config, $this->jQueryJs);
// 自定义js
$this->customJs .= Tools::getSubValue('custom_js', $config, '');
// bootstrapCSS文件路径
$this->bootstrapCss = Tools::getSubValue('bootstrap_css', $config, $this->bootstrapCss);
// 自定义CSS
$this->customCss .= Tools::getSubValue('custom_css', $config, '');
// 合并CSS
$this->_getCss();
// 合并JS
$this->_getJs();
}
/**
* 输出HTML
* @param int $type - 方法过滤,默认只获取 public类型 方法
* ReflectionMethod::IS_STATIC
* ReflectionMethod::IS_PUBLIC
* ReflectionMethod::IS_PROTECTED
* ReflectionMethod::IS_PRIVATE
* ReflectionMethod::IS_ABSTRACT
* ReflectionMethod::IS_FINAL
* @return string
*/
public function getHtml($type = \ReflectionMethod::IS_PUBLIC) {
$data = $this->getApiDoc($type);
$html = <<<EXT
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<!-- 禁止浏览器初始缩放 -->
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, maximum-scale=1, user-scalable=0">
<title>API文档 By Api-Doc-PHP</title>
{$this->customCss}
</head>
<body>
<div class="container-fluid">
<nav class="navbar navbar-expand-sm navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="#">API文档</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarColor01" >
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarColor01">
{$this->_getTopNavList($data)}
</div>
</nav>
<div class="row">
<div class="col-lg-12">{$this->_getDocList($data)}</div>
</div>
<div class="row">
<div class="col-lg-12 text-center copyright-content">
Copyright 2016 - 2018 <a href="http://www.xqitw.cn">小强IT屋</a> 版权所有
</div>
</div>
</div>
{$this->customJs}
</body>
</html>
EXT;
if (isset($_GET['download']) && $_GET['download'] === 'api_doc_php') {
Tools::downloadFile($html);
return true;
}
return $html;
}
/**
* 解析return 并生成HTML
* @param array $data
* @return string
*/
private function _getReturnData($data = []) {
$html = '';
if (!is_array($data) || count($data) < 1) {
return $html;
}
$html .= '<div class="table-item col-md-12"><p class="table-title"><span class="btn btn-sm btn-success">返回参数</span></p>';
$html .= '<table class="table"><tr><td>参数</td><td>类型</td><td>描述</td></tr>';
foreach ($data as $v) {
$html .= '<tr>
<td>' . Tools::getSubValue('return_name', $v, '') . '</td>
<td>' . Tools::getSubValue('return_type', $v, '') . '</td>
<td>' . Tools::getSubValue('return_title', $v, '') . '</td>
</tr>';
}
$html .= '</table></div>';
return $html;
}
/**
* 解析param 并生成HTML
* @param array $data
* @return string
*/
private function _getParamData($data = []) {
$html = '';
if (!is_array($data) || count($data) < 1) {
return $html;
}
$html .= '<div class="table-item col-md-12"><p class="table-title"><span class="btn btn-sm btn-danger">请求参数</span></p>';
$html .= '<table class="table"><tr><td>参数</td><td>类型</td><td>描述</td><td>默认值</td><td>是否必须</td></tr>';
foreach ($data as $v) {
$html .= '<tr>
<td>' . Tools::getSubValue('param_name', $v, '') . '</td>
<td>' . Tools::getSubValue('param_type', $v, '') . '</td>
<td>' . Tools::getSubValue('param_title', $v, '') . '</td>
<td>' . Tools::getSubValue('param_default', $v, '无默认值') . '</td>
<td>' . Tools::getSubValue('param_require', $v, '非必须') . '</td>
</tr>';
}
$html .= '</table></div>';
return $html;
}
/**
* 解析code 并生成HTML
* @param array $data
* @return string
*/
private function _getCodeData($data = []) {
$html = '';
if (!is_array($data) || count($data) < 1) {
return $html;
}
$html .= '<div class="table-item col-md-12"><p class="table-title"><span class="btn btn-sm btn-warning">状态码说明</span></p>';
$html .= '<table class="table"><tr><td>状态码</td><td>描述</td></tr>';
foreach ($data as $v) {
$html .= '<tr>
<td>' . Tools::getSubValue('code', $v, '') . '</td>
<td>' . Tools::getSubValue('content', $v, '暂无说明') . '</td>
</tr>';
}
$html .= '</table></div>';
return $html;
}
/**
* 获取指定接口操作下的文档信息
* @param $className - 类名
* @param $actionName - 操作名
* @param $actionItem - 接口数据
* @return string
*/
private function _getActionItem($className, $actionName, $actionItem) {
$html = <<<EXT
<div class="list-group-item list-group-item-action action-item col-md-12" id="{$className}_{$actionName}">
<h4 class="action-title">API - {$actionItem['title']}</h4>
<p>请求方式:
<span class="btn btn-info btn-sm">{$actionItem['method']}</span>
</p>
<p>请求地址:<a href="{$actionItem['url']}">{$actionItem['url']}</a></p>
{$this->_getParamData(Tools::getSubValue('param', $actionItem, []))}
{$this->_getReturnData(Tools::getSubValue('return', $actionItem, []))}
{$this->_getCodeData(Tools::getSubValue('code', $actionItem, []))}
</div>
EXT;
return $html;
}
/**
* 获取指定API类的文档HTML
* @param $className - 类名称
* @param $classItem - 类数据
* @return string
*/
private function _getClassItem($className, $classItem) {
$title = Tools::getSubValue('title', $classItem, '未命名');
$actionHtml = '';
if (isset($classItem['action']) && is_array($classItem['action']) && count($classItem['action']) >= 1) {
foreach ($classItem['action'] as $actionName => $actionItem) {
$actionHtml .= $this->_getActionItem($className, $actionName, $actionItem);
}
}
$html = <<<EXT
<div class="class-item" id="{$className}">
<h2 class="class-title">{$title}</h2>
<div class="list-group">{$actionHtml}</div>
</div>
EXT;
return $html;
}
/**
* 获取API文档HTML
* @param array $data - 文档数据
* @return string
*/
private function _getDocList($data) {
$html = '';
if (count($data) < 1) {
return $html;
}
$html .= '<div class="doc-content">';
foreach ($data as $className => $classItem) {
$html .= $this->_getClassItem($className, $classItem);
}
$html .= '</div>';
return $html;
}
/**
* 获取顶部导航HTML
* @param $data -API文档数据
* @return string
*/
private function _getTopNavList($data) {
$html = '<ul class="navbar-nav" id="navbar-nav-top-nav">';
foreach ($data as $className => $classItem) {
$title = Tools::getSubValue('title', $classItem, '未命名');
$html .= '<li class="nav-item dropdown">';
$html .= '<a class="nav-link dropdown-toggle" href="#" id="' . $className . '-nav" data-toggle="dropdown">' . $title . '</a>';
$html .= '<div class="dropdown-menu" aria-labelledby="' . $className . '-nav">';
foreach ($classItem['action'] as $actionName => $actionItem) {
$title = Tools::getSubValue('title', $actionItem, '未命名');
$id = $className . '_' . $actionName;
$html .= '<a class="dropdown-item" href="#' . $id . '">' . $title . '</a>';
}
$html .= '</div></li>';
}
$html .= ' <li class="nav-item"><a class="nav-link" href="?download=api_doc_php">下载文档</a></li>';
$html .= '</ul>';
return $html;
}
/**
* 获取文档CSS
* @return string
*/
private function _getCss() {
$path = realpath($this->bootstrapCss);
if (!$path || !is_file($path)) {
return $this->customCss;
}
$bootstrapCss = file_get_contents($path);
if (empty($bootstrapCss)) {
return $this->customCss;
}
$this->customCss = '<style type="text/css">' . $bootstrapCss . '</style>' . $this->customCss;
// $this->customCss = ' <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">' . $this->customCss;
return $this->customCss;
}
/**
* 获取文档JS
* @return string
*/
private function _getJs() {
// $js = '<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js" type="text/javascript"></script>';
// $js .= '<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" type="text/javascript"></script>';
// $this->customJs = $js . $this->customJs;
// return $this->customJs;
$bootstrapJs = realpath($this->bootstrapJs);
$jQueryJs = realpath($this->jQueryJs);
if (!$bootstrapJs || !$jQueryJs || !is_file($bootstrapJs) || !is_file($jQueryJs)) {
$this->customJs = '';
return $this->customCss;
}
$bootstrapJs = file_get_contents($bootstrapJs);
$jQueryJs = file_get_contents($jQueryJs);
if (empty($bootstrapJs) || empty($jQueryJs)) {
$this->customJs = '';
return $this->customJs;
}
$js = '<script type="text/javascript">' . $jQueryJs . '</script>' . '<script type="text/javascript">' . $bootstrapJs . '</script>';
$this->customJs = $js . $this->customJs;
return $this->customJs;
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* ==================================================================
* 文 件 名: ParseComment.php
* 概 要: 注释解析
* 作 者: IT小强
* 创建时间: 2018/6/5 10:38
* 修改时间:
* copyright (c) 2016 - 2018 mail@xqitw.cn
* ==================================================================
*/
namespace itxq\apidoc\lib;
/**
* 注释解析
* Class ParseComment
* @package itxq\apidoc\lib
*/
class ParseComment
{
/**
* @var array - 注释解析后的数组
*/
private $commentParams = [];
/**
* 将注释按行解析并以数组格式返回
* @param $comment - 原始注释字符串
* @return bool|array
*/
public function parseCommentToArray($comment) {
$comments = [];
if (empty($comment)) {
return $comments;
}
// 获取注释
if (preg_match('#^/\*\*(.*)\*/#s', $comment, $matches) === false) {
return $comments;
}
$matches = trim($matches[1]);
// 按行分割注释
if (preg_match_all('#^\s*\*(.*)#m', $matches, $lines) === false) {
return $comments;
}
$comments = $lines[1];
// 去除无用的注释
foreach ($comments as $k => $v) {
$comments[$k] = $v = trim($v);
if (strpos($v, '@') !== 0) {
continue;
}
$_parse = $this->_parseCommentLine($v);
if (!$_parse) {
continue;
}
$_type = $_parse['type'];
$_content = isset($_parse['content']) ? $_parse['content'] : '';
if (in_array($_type, ['param', 'code', 'return'])) {
if (!isset($this->commentParams[$_type])) {
$this->commentParams[$_type] = [];
}
unset($_parse['type']);
$this->commentParams[$_type][] = $_parse;
} else {
$this->commentParams[$_type] = $_content;
}
}
return $this->commentParams;
}
/**
* 解析注释中的参数
* @param $line - 注释行
* @return bool|array - 解析后的数组解析失败返回false
*/
private function _parseCommentLine($line) {
$line = explode(' ', $line);
$line[0] = substr($line[0], 1);
$class = new ParseLine();
$action = 'parseLine' . Tools::underlineToHump($line[0]);
if (!method_exists($class, $action)) {
$action = 'parseLineTitle';
}
return $class->$action($line);
}
}

View File

@@ -0,0 +1,73 @@
<?php
/**
* ==================================================================
* 文 件 名: ParseLine.php
* 概 要: 按行解析注释参数
* 作 者: IT小强
* 创建时间: 2018/6/5 10:34
* 修改时间:
* copyright (c) 2016 - 2018 mail@xqitw.cn
* ==================================================================
*/
namespace itxq\apidoc\lib;
/**
* 按行解析注释参数
* Class ParseLine
* @package itxq\apidoc\lib
*/
class ParseLine
{
/**
* 解析 title|url
* @param $line
* @return array
*/
public function parseLineTitle($line) {
return ['type' => isset($line[0]) ? $line[0] : '', 'content' => isset($line[1]) ? $line[1] : ''];
}
/**
* 解析 param
* @param $line
* @return array
*/
public function parseLineParam($line) {
return [
'type' => isset($line[0]) ? $line[0] : '',
'param_type' => isset($line[1]) ? $line[1] : '',
'param_name' => isset($line[2]) ? $line[2] : '',
'param_title' => isset($line[3]) ? $line[3] : '',
'param_default' => isset($line[4]) ? $line[4] : '',
'param_require' => isset($line[5]) ? $line[5] : '',
];
}
/**
* 解析 code
* @param $line
* @return array
*/
public function parseLineCode($line) {
return [
'type' => isset($line[0]) ? $line[0] : '',
'code' => isset($line[1]) ? $line[1] : '',
'content' => isset($line[2]) ? $line[2] : '',
];
}
/**
* 解析 return
* @param $line
* @return array
*/
public function parseLineReturn($line) {
return [
'type' => isset($line[0]) ? $line[0] : '',
'return_type' => isset($line[1]) ? $line[1] : '',
'return_name' => isset($line[2]) ? $line[2] : '',
'return_title' => isset($line[3]) ? $line[3] : '',
];
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* ==================================================================
* 文 件 名: Tools.php
* 概 要:
* 作 者: IT小强
* 创建时间: 2018/6/6 8:47
* 修改时间:
* copyright (c) 2016 - 2018 mail@xqitw.cn
* ==================================================================
*/
namespace itxq\apidoc\lib;
/**
* 工具类
* Class Tools
* @package itxq\apidoc\lib
*/
class Tools
{
/**
* 下划线命名转驼峰命名
* @param $str - 下划线命名字符串
* @param $isFirst - 是否为大驼峰(即首字母也大写)
* @return mixed
*/
public static function underlineToHump($str, $isFirst = false) {
$str = preg_replace_callback('/([\-\_]+([a-z]{1}))/i', function ($matches) {
return strtoupper($matches[2]);
}, $str);
if ($isFirst) {
$str = ucfirst($str);
}
return $str;
}
/**
* 驼峰命名转下划线命名
* @param $str
* @return mixed
*/
public static function humpToUnderline($str) {
$str = preg_replace_callback('/([A-Z]{1})/', function ($matches) {
return '_' . strtolower($matches[0]);
}, $str);
$str = preg_replace('/^\_/', '', $str);
return $str;
}
/**
* 获取数组、对象下标对应值,不存在时返回指定的默认值
* @param string|integer $name - 下标(键名)
* @param array|object $data - 原始数组/对象
* @param mixed $default - 指定默认值
* @return mixed
*/
public static function getSubValue($name, $data, $default = '') {
if (is_object($data)) {
$value = isset($data->$name) ? $data->$name : $default;
} else if (is_array($data)) {
$value = isset($data[$name]) ? $data[$name] : $default;
} else {
$value = $default;
}
return $value;
}
/**
* 文件下载
* @param string - $docHtml - API文档HTML内容
*/
public static function downloadFile($docHtml) {
set_time_limit(0);
//下载文件需要用到的头
header('Content-type: application/octet-stream');
header('Accept-Ranges: bytes');
header('Content-Disposition: attachment; filename=api-doc_' . date('Y-m-d') . '.html');
echo $docHtml;
exit();
}
}