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

View 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)
];
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}