20220406
This commit is contained in:
127
vendor/hg/apidoc/src/parseApi/CacheApiData.php
vendored
Normal file
127
vendor/hg/apidoc/src/parseApi/CacheApiData.php
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace hg\apidoc\parseApi;
|
||||
|
||||
|
||||
use hg\apidoc\Utils;
|
||||
use think\facade\Config;
|
||||
|
||||
class CacheApiData
|
||||
{
|
||||
protected $config = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = Config::get('apidoc');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存目录
|
||||
* @param string $appKey
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheFolder(string $appKey):string
|
||||
{
|
||||
$config = $this->config;
|
||||
$currentApps = (new Utils())->getCurrentApps($appKey);
|
||||
$configPath = !empty($config['cache']) && !empty($config['cache']['path']) ? $config['cache']['path'] : '../runtime/apidoc/';
|
||||
$cacheAppFolder = "";
|
||||
if (!empty($currentApps) && count($currentApps) > 0) {
|
||||
foreach ($currentApps as $keyIndex => $appItem) {
|
||||
$cacheAppFolder .= $appItem['folder'] . "/";
|
||||
}
|
||||
}
|
||||
$cacheFolder = $configPath . $cacheAppFolder;
|
||||
return $cacheFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定目录下缓存文件名列表
|
||||
* @param string $folder
|
||||
* @return array
|
||||
*/
|
||||
public function getCacheFileList(string $folder):array
|
||||
{
|
||||
$filePaths = glob($folder . '*.json');
|
||||
$cacheFiles = [];
|
||||
if (count($filePaths) > 0) {
|
||||
foreach ($filePaths as $item) {
|
||||
$cacheFiles[] = str_replace(".json", "", basename($item));
|
||||
}
|
||||
}
|
||||
return $cacheFiles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取接口缓存数据
|
||||
* @param string $appKey
|
||||
* @param string $cacheFileName
|
||||
* @return array|false
|
||||
*/
|
||||
public function get(string $appKey, string $cacheFileName)
|
||||
{
|
||||
$cacheFolder = $this->getCacheFolder($appKey);
|
||||
$cacheFileList = $this->getCacheFileList($cacheFolder);
|
||||
if (!file_exists($cacheFolder)) {
|
||||
return false;
|
||||
}
|
||||
if (empty($cacheFileName) && count($cacheFileList) > 0) {
|
||||
// 默认最后一个缓存文件
|
||||
$cacheFileName = $cacheFileList[count($cacheFileList) - 1];
|
||||
}
|
||||
$cacheFilePath = $cacheFolder . "/" . $cacheFileName . '.json';
|
||||
if (file_exists($cacheFilePath)) {
|
||||
// 存在缓存文件
|
||||
$fileContent = file_get_contents($cacheFilePath);
|
||||
if (empty($fileContent)) {
|
||||
return false;
|
||||
}
|
||||
$json = json_decode($fileContent);
|
||||
if (is_object($json)) {
|
||||
$json = [
|
||||
"data" => $json->data,
|
||||
"tags" => $json->tags,
|
||||
"groups" => $json->groups,
|
||||
];
|
||||
}
|
||||
return [
|
||||
'name' => $cacheFileName,
|
||||
'data' => $json,
|
||||
'list' => $cacheFileList
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置接口缓存
|
||||
* @param string $appKey
|
||||
* @param array $json
|
||||
* @return array|false
|
||||
*/
|
||||
public function set(string $appKey, array $json):array
|
||||
{
|
||||
if (empty($json)) {
|
||||
return false;
|
||||
}
|
||||
$config = $this->config;
|
||||
$fileName = date("Y-m-d H_i_s");
|
||||
$fileContent = json_encode($json);
|
||||
$cacheFolder = $this->getCacheFolder($appKey);
|
||||
$path = $cacheFolder . $fileName . ".json";
|
||||
Utils::createFile($path, $fileContent);
|
||||
$filePaths = $this->getCacheFileList($cacheFolder);
|
||||
if ($config['cache']['max'] && count($filePaths) >= $config['cache']['max']) {
|
||||
//达到最大数量,删除第一个
|
||||
$filePath = $cacheFolder . $filePaths[0] . ".json";
|
||||
Utils::delFile($filePath);
|
||||
}
|
||||
return [
|
||||
"name" => $fileName,
|
||||
"data" => $json,
|
||||
"list" => $this->getCacheFileList($cacheFolder)
|
||||
];
|
||||
}
|
||||
}
|
||||
663
vendor/hg/apidoc/src/parseApi/ParseAnnotation.php
vendored
Normal file
663
vendor/hg/apidoc/src/parseApi/ParseAnnotation.php
vendored
Normal file
@@ -0,0 +1,663 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace hg\apidoc\parseApi;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use hg\apidoc\Utils;
|
||||
use ReflectionClass;
|
||||
use Symfony\Component\ClassLoader\ClassMapGenerator;
|
||||
use think\annotation\route\Resource;
|
||||
use think\annotation\Route;
|
||||
use hg\apidoc\annotation\Group;
|
||||
use hg\apidoc\annotation\Sort;
|
||||
use hg\apidoc\annotation\Param;
|
||||
use hg\apidoc\annotation\Title;
|
||||
use hg\apidoc\annotation\Desc;
|
||||
use hg\apidoc\annotation\Author;
|
||||
use hg\apidoc\annotation\Tag;
|
||||
use hg\apidoc\annotation\Header;
|
||||
use hg\apidoc\annotation\Returned;
|
||||
use hg\apidoc\annotation\ParamType;
|
||||
use hg\apidoc\annotation\Url;
|
||||
use hg\apidoc\annotation\Method;
|
||||
use think\annotation\route\Group as RouteGroup;
|
||||
use think\facade\App;
|
||||
use think\facade\Config;
|
||||
|
||||
class ParseAnnotation
|
||||
{
|
||||
|
||||
protected $config = [];
|
||||
|
||||
protected $reader;
|
||||
|
||||
//tags,当前应用/版本所有的tag
|
||||
protected $tags = array();
|
||||
|
||||
//groups,当前应用/版本的分组name
|
||||
protected $groups = array();
|
||||
|
||||
protected $controller_layer = "";
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->reader = new AnnotationReader();
|
||||
$this->config = Config::get('apidoc')?Config::get('apidoc'):Config::get('apidoc.');
|
||||
$this->controller_layer = Config::get('route.controller_layer',"controller");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成api接口数据
|
||||
* @param string $appKey
|
||||
* @return array
|
||||
*/
|
||||
public function renderApiData(string $appKey): array
|
||||
{
|
||||
$config = $this->config;
|
||||
$currentApps = (new Utils())->getCurrentApps($appKey);
|
||||
$currentApp = $currentApps[count($currentApps) - 1];
|
||||
|
||||
if (!empty($config['controllers']) && count($config['controllers']) > 0) {
|
||||
// 配置的控制器列表
|
||||
$controllers = $this->getConfigControllers($currentApp['path']);
|
||||
} else {
|
||||
// 默认读取所有的
|
||||
$controllers = $this->getDirControllers($currentApp['path']);
|
||||
}
|
||||
$apiData = [];
|
||||
if (!empty($controllers) && count($controllers) > 0) {
|
||||
foreach ($controllers as $class) {
|
||||
$classData = $this->parseController($class);
|
||||
if ($classData !== false) {
|
||||
$apiData[] = $classData;
|
||||
}
|
||||
}
|
||||
}
|
||||
$json = array(
|
||||
"data" => $apiData,
|
||||
"tags" => $this->tags,
|
||||
"groups" => $this->groups
|
||||
);
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生成文档的控制器列表
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
protected function getConfigControllers(string $path): array
|
||||
{
|
||||
$config = $this->config;
|
||||
$controllers = [];
|
||||
|
||||
$configControllers = $config['controllers'];
|
||||
if (!empty($configControllers) && count($configControllers) > 0) {
|
||||
foreach ($configControllers as $item) {
|
||||
if ( strpos($item, $path) !== false && class_exists($item)) {
|
||||
$controllers[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $controllers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目录下的控制器列表
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
protected function getDirControllers(string $path): array
|
||||
{
|
||||
if ($path) {
|
||||
if (strpos(App::getRootPath(), '/') !== false) {
|
||||
$pathStr = str_replace("\\", "/", $path);
|
||||
} else {
|
||||
$pathStr = $path;
|
||||
}
|
||||
$dir = App::getRootPath() . $pathStr;
|
||||
} else {
|
||||
$dir = App::getRootPath() . $this->controller_layer;
|
||||
}
|
||||
$controllers = [];
|
||||
if (is_dir($dir)) {
|
||||
$controllers = $this->scanDir($dir, $path);
|
||||
}
|
||||
return $controllers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理指定目录下的控制器
|
||||
* @param string $dir
|
||||
* @param string $appPath
|
||||
* @return array
|
||||
*/
|
||||
protected function scanDir(string $dir, string $appPath): array
|
||||
{
|
||||
$list = [];
|
||||
foreach (ClassMapGenerator::createMap($dir) as $class => $path) {
|
||||
if (
|
||||
!isset($this->config['filter_controllers']) ||
|
||||
(isset($this->config['filter_controllers']) && !in_array($class, $this->config['filter_controllers'])) &&
|
||||
$this->config['definitions'] != $class
|
||||
) {
|
||||
if (strpos($class, '\\') === false) {
|
||||
$list[] = $appPath . "\\" . $class;
|
||||
} else {
|
||||
$list[] = $class;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
protected function parseController($class)
|
||||
{
|
||||
$config = $this->config;
|
||||
$data = [];
|
||||
$refClass = new ReflectionClass($class);
|
||||
$classTextAnnotations = $this->parseTextAnnotation($refClass);
|
||||
if (in_array("NotParse", $classTextAnnotations)) {
|
||||
return false;
|
||||
}
|
||||
$title = $this->reader->getClassAnnotation($refClass, Title::class);
|
||||
$group = $this->reader->getClassAnnotation($refClass, Group::class);
|
||||
$sort = $this->reader->getClassAnnotation($refClass, Sort::class);
|
||||
|
||||
$routeGroup = $this->reader->getClassAnnotation($refClass, RouteGroup::class);
|
||||
$controllersNameArr = explode("\\", $class);
|
||||
$controllersName = $controllersNameArr[count($controllersNameArr) - 1];
|
||||
$data['controller'] = $controllersName;
|
||||
$data['group'] = !empty($group->value) ? $group->value : null;
|
||||
$data['sort'] = !empty($sort->value) ? $sort->value : null;
|
||||
if (!empty($data['group']) && !in_array($data['group'], $this->groups)) {
|
||||
$this->groups[] = $data['group'];
|
||||
}
|
||||
$data['title'] = !empty($title) && !empty($title->value) ? $title->value : "";
|
||||
|
||||
if (empty($title)) {
|
||||
if (!empty($classTextAnnotations) && count($classTextAnnotations) > 0) {
|
||||
$data['title'] = $classTextAnnotations[0];
|
||||
} else {
|
||||
$data['title'] = $controllersName;
|
||||
}
|
||||
}
|
||||
$methodList = [];
|
||||
$filter_method = !empty($config['filter_method']) ? $config['filter_method'] : [];
|
||||
$data['menu_key'] = $data['controller'] . "_" . mt_rand(10000, 99999);
|
||||
|
||||
foreach ($refClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $refMethod) {
|
||||
if (!empty($refMethod->name) && !in_array($refMethod->name, $filter_method)) {
|
||||
$methodItem = $this->parseAnnotation($refMethod, true,"controller");
|
||||
if (!count((array)$methodItem)) {
|
||||
continue;
|
||||
}
|
||||
$textAnnotations = $this->parseTextAnnotation($refMethod);
|
||||
// 标注不解析的方法
|
||||
if (in_array("NotParse", $textAnnotations)) {
|
||||
continue;
|
||||
}
|
||||
// 无标题,且有文本注释
|
||||
if (empty($methodItem['title']) && !empty($textAnnotations) && count($textAnnotations) > 0) {
|
||||
$methodItem['title'] = $textAnnotations[0];
|
||||
}
|
||||
// 添加统一headers请求头参数
|
||||
if (!empty($config['headers']) && !in_array("NotHeaders", $textAnnotations)) {
|
||||
if (!empty($methodItem['header'])) {
|
||||
$methodItem['header'] = Utils::arrayMergeAndUnique("name", $config['headers'], $methodItem['header']);
|
||||
} else {
|
||||
$methodItem['header'] = $config['headers'];
|
||||
}
|
||||
}
|
||||
// 添加统一params请求参数
|
||||
if (!empty($config['parameters']) && !in_array("NotParameters", $textAnnotations)) {
|
||||
if (!empty($methodItem['param'])) {
|
||||
$methodItem['param'] = Utils::arrayMergeAndUnique("name", $config['parameters'], $methodItem['param']);
|
||||
} else {
|
||||
$methodItem['param'] = $config['parameters'];
|
||||
}
|
||||
}
|
||||
// 添加responses统一响应体
|
||||
if (
|
||||
!empty($config['responses']) &&
|
||||
!is_string($config['responses']) &&
|
||||
!in_array("NotResponses", $textAnnotations)
|
||||
) {
|
||||
// 显示在响应体中
|
||||
$returned = [];
|
||||
$hasMian = false;
|
||||
if (isset($config['responses']['data']) && !$config['responses']['show_responses']) {
|
||||
$responsesData = [];
|
||||
} else if (isset($config['responses']['data']) && $config['responses']['show_responses'] === true) {
|
||||
$responsesData = $config['responses']['data'];
|
||||
} else {
|
||||
$responsesData = $config['responses'];
|
||||
}
|
||||
// 合并统一响应体及响应参数相同的字段
|
||||
$returnData = [];
|
||||
$resKeys = [];
|
||||
foreach ($responsesData as $resItem) {
|
||||
$resKeys[]=$resItem['name'];
|
||||
}
|
||||
foreach ($methodItem['return'] as $returnItem){
|
||||
if (!(in_array($returnItem['name'],$resKeys) && $returnItem['source']==='controller' && !empty($returnItem['replaceGlobal']))){
|
||||
$returnData[]=$returnItem;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($responsesData as $resItem) {
|
||||
$resData = $resItem;
|
||||
$globalFind = Utils::getArrayFind($methodItem['return'],function ($item)use ($resItem){
|
||||
if ($item['name'] == $resItem['name'] && $item['source']==='controller' && !empty($item['replaceGlobal'])){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (!empty($globalFind)){
|
||||
$resData = array_merge($resItem,$globalFind);
|
||||
}else if (!empty($resData['main']) && $resData['main'] === true) {
|
||||
$resData['params'] = $returnData;
|
||||
|
||||
$resData['resKeys']=$resKeys;
|
||||
$hasMian = true;
|
||||
}
|
||||
$resData['find'] =$globalFind;
|
||||
$returned[] = $resData;
|
||||
}
|
||||
if (!$hasMian) {
|
||||
$returned = Utils::arrayMergeAndUnique("name", $returned, $methodItem['return']);
|
||||
}
|
||||
$methodItem['return'] = $returned;
|
||||
}
|
||||
// 默认method
|
||||
if (empty($methodItem['method'])) {
|
||||
$methodItem['method'] = !empty($config['default_method']) ? $config['default_method'] : 'GET';
|
||||
}
|
||||
// 默认default_author
|
||||
if (empty($methodItem['author']) && !empty($config['default_author']) && !in_array("NotDefaultAuthor", $textAnnotations)) {
|
||||
$methodItem['author'] = $config['default_author'];
|
||||
}
|
||||
|
||||
// Tags
|
||||
if (!empty($methodItem['tag'])) {
|
||||
if (strpos($methodItem['tag'], ' ') !== false) {
|
||||
$tagArr = explode(" ", $methodItem['tag']);
|
||||
foreach ($tagArr as $tag) {
|
||||
if (!in_array($tag, $this->tags)) {
|
||||
$this->tags[] = $tag;
|
||||
}
|
||||
}
|
||||
} else if (!in_array($methodItem['tag'], $this->tags)) {
|
||||
$this->tags[] = $methodItem['tag'];
|
||||
}
|
||||
}
|
||||
|
||||
// 无url,自动生成
|
||||
if (empty($methodItem['url'])) {
|
||||
$methodItem['url'] = $this->autoCreateUrl($refMethod);
|
||||
} else if (!empty($routeGroup->value)) {
|
||||
// 路由分组,url加上分组
|
||||
$methodItem['url'] = '/' . $routeGroup->value . '/' . $methodItem['url'];
|
||||
}
|
||||
if (!empty($methodItem['url']) && substr($methodItem['url'], 0, 1) != "/") {
|
||||
$methodItem['url'] = "/" . $methodItem['url'];
|
||||
}
|
||||
$methodItem['name'] = $refMethod->name;
|
||||
$methodItem['menu_key'] = $methodItem['method'] . "_" . $refMethod->name . "_" . mt_rand(10000, 99999);
|
||||
|
||||
$methodList[] = $methodItem;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
$data['children'] = $methodList;
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动生成url
|
||||
* @param $method
|
||||
* @return string
|
||||
*/
|
||||
protected function autoCreateUrl($method): string
|
||||
{
|
||||
if (Config::get("controller_auto_search") || !empty($this->config['controller_auto_search'])){
|
||||
$pathArr = explode("\\", $method->class );
|
||||
}else{
|
||||
$searchString = $this->controller_layer . '\\';
|
||||
$substr = substr(strstr($method->class, $searchString), strlen($searchString));
|
||||
$pathArr = explode("\\", str_replace($substr, str_replace('\\', '.', $substr), $method->class));
|
||||
}
|
||||
// 控制器地址
|
||||
$filterPathNames = array(App::getNamespace(), $this->controller_layer);
|
||||
$classPathArr = [];
|
||||
foreach ($pathArr as $item) {
|
||||
if (!in_array($item, $filterPathNames)) {
|
||||
if (!empty($this->config['auto_url_rule'])){
|
||||
switch ($this->config['auto_url_rule']) {
|
||||
case 'lcfirst':
|
||||
$classPathArr[] = lcfirst($item);
|
||||
break;
|
||||
case 'ucfirst':
|
||||
$classPathArr[] = ucfirst($item);
|
||||
break;
|
||||
default:
|
||||
$classPathArr[] = $item;
|
||||
}
|
||||
}else{
|
||||
$classPathArr[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
$classPath = implode('/', $classPathArr);
|
||||
return '/' . $classPath . '/' . $method->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* ref引用
|
||||
* @param $refPath
|
||||
* @param bool $enableRefService
|
||||
* @return false|string[]
|
||||
*/
|
||||
protected function renderRef(string $refPath, bool $enableRefService = true): array
|
||||
{
|
||||
$res = ['type' => 'model'];
|
||||
// 通用定义引入
|
||||
if (strpos($refPath, '\\') === false) {
|
||||
$config = $this->config;
|
||||
$refPath = $config['definitions'] . '\\' . $refPath;
|
||||
$data = $this->renderService($refPath);
|
||||
$res['type'] = "service";
|
||||
$res['data'] = $data;
|
||||
return $res;
|
||||
}
|
||||
// 模型引入
|
||||
$modelData = (new ParseModel($this->reader))->renderModel($refPath);
|
||||
if ($modelData !== false) {
|
||||
$res['data'] = $modelData;
|
||||
return $res;
|
||||
}
|
||||
if ($enableRefService === false) {
|
||||
return false;
|
||||
}
|
||||
$data = $this->renderService($refPath);
|
||||
$res['type'] = "service";
|
||||
$res['data'] = $data;
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析注释引用
|
||||
* @param $refPath
|
||||
* @return array
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function renderService(string $refPath)
|
||||
{
|
||||
$pathArr = explode("\\", $refPath);
|
||||
$methodName = $pathArr[count($pathArr) - 1];
|
||||
unset($pathArr[count($pathArr) - 1]);
|
||||
$classPath = implode("\\", $pathArr);
|
||||
$classReflect = new \ReflectionClass($classPath);
|
||||
$methodName = trim($methodName);
|
||||
$refMethod = $classReflect->getMethod($methodName);
|
||||
$res = $this->parseAnnotation($refMethod, true);
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Param/Returned的字段名name、params子级参数
|
||||
* @param $values
|
||||
* @return array
|
||||
*/
|
||||
protected function handleParamValue($values, string $field = 'param'): array
|
||||
{
|
||||
$name = "";
|
||||
$params = [];
|
||||
if (!empty($values) && is_array($values) && count($values) > 0) {
|
||||
foreach ($values as $item) {
|
||||
if (is_string($item)) {
|
||||
$name = $item;
|
||||
} else if (is_object($item)) {
|
||||
if (!empty($item->ref)) {
|
||||
$refRes = $this->renderRef($item->ref, true);
|
||||
$params = $this->handleRefData($params, $refRes, $item, $field);
|
||||
} else {
|
||||
$param = [
|
||||
"name" => "",
|
||||
"type" => $item->type,
|
||||
"desc" => $item->desc,
|
||||
"default" => $item->default,
|
||||
"require" => $item->require,
|
||||
"childrenType"=> $item->childrenType
|
||||
];
|
||||
$children = $this->handleParamValue($item->value);
|
||||
$param['name'] = $children['name'];
|
||||
if (count($children['params']) > 0) {
|
||||
$param['params'] = $children['params'];
|
||||
}
|
||||
$params[] = $param;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$name = $values;
|
||||
}
|
||||
return ['name' => $name, 'params' => $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析方法注释
|
||||
* @param $refMethod
|
||||
* @param bool $enableRefService 是否终止service的引入
|
||||
* @param string $source 注解来源
|
||||
* @return array
|
||||
*/
|
||||
protected function parseAnnotation($refMethod, bool $enableRefService = true,$source=""): array
|
||||
{
|
||||
$data = [];
|
||||
if ($annotations = $this->reader->getMethodAnnotations($refMethod)) {
|
||||
$headers = [];
|
||||
$params = [];
|
||||
$returns = [];
|
||||
|
||||
foreach ($annotations as $annotation) {
|
||||
switch (true) {
|
||||
case $annotation instanceof Param:
|
||||
$params = $this->handleParamAndReturned($params,$annotation,'param',$enableRefService);
|
||||
break;
|
||||
case $annotation instanceof Returned:
|
||||
|
||||
$returns = $this->handleParamAndReturned($returns,$annotation,'return',$enableRefService,$source);
|
||||
break;
|
||||
case $annotation instanceof Header:
|
||||
if (!empty($annotation->ref)) {
|
||||
$refRes = $this->renderRef($annotation->ref, $enableRefService);
|
||||
$headers = $this->handleRefData($headers, $refRes, $annotation, 'header');
|
||||
} else {
|
||||
$param = [
|
||||
"name" => $annotation->value,
|
||||
"desc" => $annotation->desc,
|
||||
"require" => $annotation->require,
|
||||
"type" => $annotation->type,
|
||||
"default" => $annotation->default,
|
||||
];
|
||||
$headers[] = $param;
|
||||
}
|
||||
break;
|
||||
case $annotation instanceof Route:
|
||||
if (empty($data['method'])) {
|
||||
$data['method'] = $annotation->method;
|
||||
}
|
||||
if (empty($data['url'])) {
|
||||
$data['url'] = $annotation->value;
|
||||
}
|
||||
break;
|
||||
case $annotation instanceof Author:
|
||||
$data['author'] = $annotation->value;
|
||||
break;
|
||||
|
||||
case $annotation instanceof Title:
|
||||
$data['title'] = $annotation->value;
|
||||
break;
|
||||
case $annotation instanceof Desc:
|
||||
$data['desc'] = $annotation->value;
|
||||
break;
|
||||
case $annotation instanceof ParamType:
|
||||
$data['paramType'] = $annotation->value;
|
||||
break;
|
||||
case $annotation instanceof Url:
|
||||
$data['url'] = $annotation->value;
|
||||
break;
|
||||
case $annotation instanceof Method:
|
||||
$data['method'] = $annotation->value;
|
||||
break;
|
||||
case $annotation instanceof Tag:
|
||||
$data['tag'] = $annotation->value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($headers && count($headers) > 0) {
|
||||
$data['header'] = $headers;
|
||||
}
|
||||
$data['param'] = $params;
|
||||
$data['return'] = $returns;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理请求参数与返回参数
|
||||
* @param $params
|
||||
* @param $annotation
|
||||
* @param string $type
|
||||
* @param false $enableRefService
|
||||
* @param string $source 注解来源
|
||||
* @return array
|
||||
*/
|
||||
protected function handleParamAndReturned($params,$annotation,$type="param",$enableRefService=false,$source=""){
|
||||
if (!empty($annotation->ref)) {
|
||||
$refRes = $this->renderRef($annotation->ref, $enableRefService);
|
||||
$params = $this->handleRefData($params, $refRes, $annotation, $type,$source);
|
||||
} else {
|
||||
$param = [
|
||||
"name" => "",
|
||||
"type" => $annotation->type,
|
||||
"desc" => $annotation->desc,
|
||||
"default" => $annotation->default,
|
||||
"require" => $annotation->require,
|
||||
"childrenType" => $annotation->childrenType,
|
||||
"source" => $source,
|
||||
"replaceGlobal" =>!empty($annotation->replaceGlobal)?$annotation->replaceGlobal:""
|
||||
];
|
||||
$children = $this->handleParamValue($annotation->value, $type);
|
||||
$param['name'] = $children['name'];
|
||||
if (count($children['params']) > 0) {
|
||||
$param['params'] = $children['params'];
|
||||
if ($annotation->type === 'tree' && !empty($annotation->childrenField)) {
|
||||
// 类型为tree的
|
||||
$param['params'][] = [
|
||||
'params' => $children['params'],
|
||||
'name' => $annotation->childrenField,
|
||||
'type' => 'array',
|
||||
'desc' => $annotation->childrenDesc,
|
||||
];
|
||||
}
|
||||
}
|
||||
// 合并同级已有的字段
|
||||
$params = Utils::arrayMergeAndUnique("name", $params, [$param]);
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析非注解文本注释
|
||||
* @param $refMethod
|
||||
* @return array|false
|
||||
*/
|
||||
protected function parseTextAnnotation($refMethod): array
|
||||
{
|
||||
$annotation = $refMethod->getDocComment();
|
||||
if (empty($annotation)) {
|
||||
return [];
|
||||
}
|
||||
if (preg_match('#^/\*\*(.*)\*/#s', $annotation, $comment) === false)
|
||||
return [];
|
||||
$comment = trim($comment [1]);
|
||||
if (preg_match_all('#^\s*\*(.*)#m', $comment, $lines) === false)
|
||||
return [];
|
||||
$data = [];
|
||||
foreach ($lines[1] as $line) {
|
||||
$line = trim($line);
|
||||
if (!empty ($line) && strpos($line, '@') !== 0) {
|
||||
$data[] = $line;
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理param、returned 参数
|
||||
* @param $params
|
||||
* @param $refRes
|
||||
* @param $annotation
|
||||
* @param string|null $source 注解来源
|
||||
* @return array
|
||||
*/
|
||||
protected function handleRefData($params, $refRes, $annotation, string $field,$source=""): array
|
||||
{
|
||||
if ($refRes['type'] === "model" && count($refRes['data']) > 0) {
|
||||
// 模型引入
|
||||
$data = $refRes['data'];
|
||||
} else if ($refRes['type'] === "service" && !empty($refRes['data']) && !empty($refRes['data'][$field])) {
|
||||
// service引入
|
||||
$data = $refRes['data'][$field];
|
||||
} else {
|
||||
return $params;
|
||||
}
|
||||
// 过滤field
|
||||
if (!empty($annotation->field)) {
|
||||
$data = (new Utils())->filterParamsField($data, $annotation->field, 'field');
|
||||
}
|
||||
// 过滤withoutField
|
||||
if (!empty($annotation->withoutField)) {
|
||||
$data = (new Utils())->filterParamsField($data, $annotation->withoutField, 'withoutField');
|
||||
}
|
||||
|
||||
if (!empty($annotation->value)) {
|
||||
$item = [
|
||||
'name' => $annotation->value,
|
||||
'desc' => $annotation->desc,
|
||||
'type' => $annotation->type,
|
||||
'require' => $annotation->require,
|
||||
'default' => $annotation->default,
|
||||
'params' => $data,
|
||||
'source'=>$source,
|
||||
"replaceGlobal" =>!empty($annotation->replaceGlobal)?$annotation->replaceGlobal:""
|
||||
];
|
||||
$children = $this->handleParamValue($annotation->value, 'param');
|
||||
$item['name'] = $children['name'];
|
||||
if (count($children['params']) > 0) {
|
||||
$item['params'] = Utils::arrayMergeAndUnique("name",$data,$children['params']);
|
||||
if ($annotation->type === 'tree' && !empty($annotation->childrenField)) {
|
||||
// 类型为tree的
|
||||
$item['params'][] = [
|
||||
'params' => $item['params'],
|
||||
'name' => $annotation->childrenField,
|
||||
'type' => 'array',
|
||||
'desc' => $annotation->childrenDesc,
|
||||
];
|
||||
}
|
||||
}
|
||||
$params[] = $item;
|
||||
|
||||
|
||||
} else {
|
||||
$params = array_merge($params, $data);
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
71
vendor/hg/apidoc/src/parseApi/ParseMarkdown.php
vendored
Normal file
71
vendor/hg/apidoc/src/parseApi/ParseMarkdown.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace hg\apidoc\parseApi;
|
||||
|
||||
use think\facade\App;
|
||||
use hg\apidoc\Utils;
|
||||
use think\facade\Config;
|
||||
|
||||
class ParseMarkdown
|
||||
{
|
||||
protected $config = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = Config::get('apidoc')?Config::get('apidoc'):Config::get('apidoc.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取md文档菜单
|
||||
* @return array
|
||||
*/
|
||||
public function getDocsMenu(): array
|
||||
{
|
||||
$config = $this->config;
|
||||
$docData = [];
|
||||
if (!empty($config['docs']) && !empty($config['docs']['menus']) && count($config['docs']['menus']) > 0) {
|
||||
$docData = $this->handleDocsMenuData($config['docs']['menus']);
|
||||
}
|
||||
return $docData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理md文档菜单数据
|
||||
* @param array $menus
|
||||
* @return array
|
||||
*/
|
||||
protected function handleDocsMenuData(array $menus): array
|
||||
{
|
||||
$list = [];
|
||||
foreach ($menus as $item) {
|
||||
if (!empty($item['items']) && count($item['items']) > 0) {
|
||||
$item['items'] = $this->handleDocsMenuData($item['items']);
|
||||
$item['group'] = 'markdown_doc';
|
||||
$item['menu_key'] = "md_group_" . mt_rand(10000, 99999);
|
||||
$list[] = $item;
|
||||
} else {
|
||||
$item['type'] = 'md';
|
||||
$item['menu_key'] = "md_" . mt_rand(10000, 99999);
|
||||
$list[] = $item;
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取md文档内容
|
||||
* @param string $appKey
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function getContent(string $appKey, string $path): string
|
||||
{
|
||||
$currentApps = (new Utils())->getCurrentApps($appKey);
|
||||
$mdPath = (new Utils())->replaceCurrentAppTemplate($path, $currentApps);
|
||||
$filePath = App::getRootPath() . $mdPath . '.md';
|
||||
$contents = Utils::getFileContent($filePath);
|
||||
return $contents;
|
||||
}
|
||||
}
|
||||
236
vendor/hg/apidoc/src/parseApi/ParseModel.php
vendored
Normal file
236
vendor/hg/apidoc/src/parseApi/ParseModel.php
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace hg\apidoc\parseApi;
|
||||
|
||||
use Doctrine\Common\Annotations\Reader;
|
||||
use think\Db as Db5;
|
||||
use think\facade\Db;
|
||||
use hg\apidoc\annotation\Field;
|
||||
use hg\apidoc\annotation\WithoutField;
|
||||
use hg\apidoc\annotation\AddField;
|
||||
use think\helper\Str;
|
||||
use hg\apidoc\Utils;
|
||||
|
||||
class ParseModel
|
||||
{
|
||||
protected $reader;
|
||||
|
||||
public function __construct(Reader $reader)
|
||||
{
|
||||
$this->reader = $reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成模型数据
|
||||
* @param string $path
|
||||
* @return array|false
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function renderModel(string $path)
|
||||
{
|
||||
$modelClassArr = explode("\\", $path);
|
||||
$modelActionName = $modelClassArr[count($modelClassArr) - 1];
|
||||
$modelClassName = $modelClassArr[count($modelClassArr) - 2];
|
||||
unset($modelClassArr[count($modelClassArr) - 1]);
|
||||
$modelClassPath = implode("\\", $modelClassArr);
|
||||
$classReflect = new \ReflectionClass($modelClassPath);
|
||||
$modelActionName = trim($modelActionName);
|
||||
$methodAction = $classReflect->getMethod($modelActionName);
|
||||
// 获取所有模型属性
|
||||
$propertys = $classReflect->getDefaultProperties();
|
||||
|
||||
// 获取表字段
|
||||
$model = $this->getModel($methodAction, $modelClassName);
|
||||
if (!is_callable(array($model, 'getTable'))) {
|
||||
return false;
|
||||
}
|
||||
$table = $this->getTableDocument($model, $propertys);
|
||||
|
||||
// 模型注释-field
|
||||
if ($fieldAnnotations = $this->reader->getMethodAnnotation($methodAction, Field::class)) {
|
||||
$table = (new Utils())->filterParamsField($table, $fieldAnnotations->value, 'field');
|
||||
}
|
||||
// 模型注释-withoutField
|
||||
if ($fieldAnnotations = $this->reader->getMethodAnnotation($methodAction, WithoutField::class)) {
|
||||
$table = (new Utils())->filterParamsField($table, $fieldAnnotations->value, 'withoutField');
|
||||
}
|
||||
// 模型注释-addField
|
||||
if ($annotations = $this->reader->getMethodAnnotations($methodAction)) {
|
||||
foreach ($annotations as $annotation) {
|
||||
switch (true) {
|
||||
case $annotation instanceof AddField:
|
||||
$param = [
|
||||
"name" => "",
|
||||
"desc" => $annotation->desc,
|
||||
"require" => $annotation->require,
|
||||
"type" => $annotation->type,
|
||||
"default" => $annotation->default
|
||||
];
|
||||
$children = $this->handleParamValue($annotation->value);
|
||||
$param['name'] = $children['name'];
|
||||
if (count($children['params']) > 0) {
|
||||
$param['params'] = $children['params'];
|
||||
}
|
||||
$isExists = false;
|
||||
$newTable = [];
|
||||
foreach ($table as $item) {
|
||||
if ($param['name'] === $item['name']) {
|
||||
$isExists = true;
|
||||
$newTable[] = $param;
|
||||
} else {
|
||||
$newTable[] = $item;
|
||||
}
|
||||
}
|
||||
$table = $newTable;
|
||||
if (!$isExists) {
|
||||
$table[] = $param;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理字段参数
|
||||
* @param $values
|
||||
* @return array
|
||||
*/
|
||||
protected function handleParamValue($values): array
|
||||
{
|
||||
$name = "";
|
||||
$params = [];
|
||||
if (!empty($values) && is_array($values) && count($values) > 0) {
|
||||
foreach ($values as $item) {
|
||||
if (is_string($item)) {
|
||||
$name = $item;
|
||||
} else if (is_object($item)) {
|
||||
$param = [
|
||||
"name" => "",
|
||||
"type" => $item->type,
|
||||
"desc" => $item->desc,
|
||||
"default" => $item->default,
|
||||
"require" => $item->require,
|
||||
];
|
||||
$children = $this->handleParamValue($item->value);
|
||||
$param['name'] = $children['name'];
|
||||
if (count($children['params']) > 0) {
|
||||
$param['params'] = $children['params'];
|
||||
}
|
||||
$params[] = $param;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$name = $values;
|
||||
}
|
||||
return ['name' => $name, 'params' => $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型实例
|
||||
* @param $method
|
||||
* @param $modelClassName
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getModel($method, $modelClassName)
|
||||
{
|
||||
if (!empty($method->class)) {
|
||||
$relationModelClass = $this->getIncludeClassName($method->class, $modelClassName);
|
||||
if ($relationModelClass) {
|
||||
$modelInstance = new $relationModelClass();
|
||||
return $modelInstance;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取模型类
|
||||
* @param $mainClass
|
||||
* @param $class
|
||||
* @return string
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function getIncludeClassName($mainClass, $class)
|
||||
{
|
||||
$classReflect = new \ReflectionClass($mainClass);
|
||||
$possibleClass = $classReflect->getNamespaceName() . "\\" . $class;
|
||||
if (class_exists($possibleClass)) {
|
||||
return $possibleClass;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型注解数据
|
||||
* @param $model
|
||||
* @param $propertys
|
||||
* @return array
|
||||
*/
|
||||
public function getTableDocument($model,array $propertys):array
|
||||
{
|
||||
|
||||
$tp_version = \think\facade\App::version();
|
||||
if (substr($tp_version, 0, 2) == '5.'){
|
||||
$createSQL = Db5::query("show create table " . $model->getTable())[0]['Create Table'];
|
||||
}else{
|
||||
$createSQL = Db::query("show create table " . $model->getTable())[0]['Create Table'];
|
||||
}
|
||||
// $createSQL = Db::query("show create table " . $model->getTable())[0]['Create Table'];
|
||||
preg_match_all("#[^KEY]`(.*?)` (.*?) (.*?),\n#", $createSQL, $matches);
|
||||
$fields = $matches[1];
|
||||
$types = $matches[2];
|
||||
$contents = $matches[3];
|
||||
$fieldComment = [];
|
||||
//组织注释
|
||||
for ($i = 0; $i < count($matches[0]); $i++) {
|
||||
$key = $fields[$i];
|
||||
$type = $types[$i];
|
||||
$default = "";
|
||||
$require = "0";
|
||||
$desc = "";
|
||||
$content = $contents[$i];
|
||||
if (strpos($type, '(`') !== false) {
|
||||
continue;
|
||||
}
|
||||
if (strpos($content, 'COMMENT') !== false) {
|
||||
// 存在字段注释
|
||||
preg_match_all("#COMMENT\s*'(.*?)'#", $content, $edscs);
|
||||
if (!empty($edscs[1]) && !empty($edscs[1][0]))
|
||||
$desc = $edscs[1][0];
|
||||
}
|
||||
if (strpos($content, 'DEFAULT') !== false) {
|
||||
// 存在字段默认值
|
||||
preg_match_all("#DEFAULT\s*'(.*?)'#", $content, $defaults);
|
||||
$default = $defaults[1] && is_array($defaults[1])?$defaults[1][0]:"";
|
||||
}
|
||||
|
||||
if (strpos($content, 'NOT NULL') !== false) {
|
||||
// 必填字段
|
||||
$require = "1";
|
||||
}
|
||||
|
||||
$name = $key;
|
||||
// 转换字段名为驼峰命名(用于输出)
|
||||
if (isset($propertys['convertNameToCamel']) && $propertys['convertNameToCamel'] === true) {
|
||||
$name = Str::camel($key);
|
||||
}
|
||||
$fieldComment[] = [
|
||||
"name" => $name,
|
||||
"type" => $type,
|
||||
"desc" => $desc,
|
||||
"default" => $default,
|
||||
"require" => $require,
|
||||
];
|
||||
}
|
||||
return $fieldComment;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user