123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- <?php
- // +----------------------------------------------------------------------
- // | Yzncms [ 御宅男工作室 ]
- // +----------------------------------------------------------------------
- // | Copyright (c) 2018 http://yzncms.com All rights reserved.
- // +----------------------------------------------------------------------
- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
- // +----------------------------------------------------------------------
- // | Author: 御宅男 <530765310@qq.com>
- // +----------------------------------------------------------------------
-
- // +----------------------------------------------------------------------
- // | 接口管理
- // +----------------------------------------------------------------------
- namespace addons\apidoc\library;
-
- use think\Db as Db5;
- use think\facade\Db;
-
- class Parser
- {
- protected $config = [];
-
- /**
- * 架构方法 设置参数
- * @param array $config 配置参数
- */
- public function __construct($config = [])
- {
- $this->config = array_merge($this->config, $config);
- }
-
- /**
- * 解析class类的注释
- * @param string $doc
- * @return array
- */
- public function parseClass($doc = '')
- {
- if ($doc == '') {
- return false;
- }
- // Get the comment
- if (preg_match('#^/\*\*(.*)\*/#s', $doc, $comment) === false) {
- return false;
- }
-
- $comment = trim($comment[1]);
- // Get all the lines and strip the * from the first character
- if (preg_match_all('#^\s*\*(.*)#m', $comment, $lines) === false) {
- return false;
- }
-
- $res = $this->parseClassLines($lines[1]);
-
- return $res;
- }
-
- /**
- * 解析class类的注释,将每条字符串,解析成key,value对象
- * @param $lines
- * @return array|bool
- */
- private function parseClassLines($lines)
- {
- $desc = [];
- foreach ($lines as $line) {
- $line = trim($line);
- if (empty($line)) {
- return false; // Empty line
- }
- if (strpos($line, '@') === 0) {
- if (strpos($line, ' ') > 0) {
- // Get the parameter name
- $param = substr($line, 1, strpos($line, ' ') - 1);
- $value = substr($line, strlen($param) + 2); // Get the value
- } else {
- $param = substr($line, 1);
- $value = '';
- }
-
- $desc[$param] = $value;
-
- }
-
- }
- return $desc;
- }
-
- /**
- * 解析控制器方法的注释
- * @param string $doc
- * @return array
- */
- public function parseAction($doc = '')
- {
- if ($doc == '') {
- return false;
- }
- // Get the comment
- if (preg_match('#^/\*\*(.*)\*/#s', $doc, $comment) === false) {
- return false;
- }
-
- $comment = trim($comment[1]);
- // Get all the lines and strip the * from the first character
- if (preg_match_all('#^\s*\*(.*)#m', $comment, $lines) === false) {
- return false;
- }
-
- $res = $this->parseActionLines($lines[1]);
-
- return $res;
- }
-
- /**
- * 解析方法的注释,将每条字符串,解析成key,value对象,并处理
- * @param $lines
- * @return array|bool
- */
- private function parseActionLines($lines)
- {
- $desc = [];
- foreach ($lines as $line) {
- $line = trim($line);
- if (!empty($line)) {
- if (strpos($line, '@') === 0) {
- if (strpos($line, ' ') > 0) {
- // Get the parameter name
- $param = substr($line, 1, strpos($line, ' ') - 1);
- $value = substr($line, strlen($param) + 2); // Get the value
- } else {
- $param = substr($line, 1);
- $value = '';
- }
-
- if ($param == 'param') {
- $valueObj = $this->formatParam($value);
- if (!empty($valueObj['params']) && empty($valueObj['name'])) {
- // 只配置参数,没配置name则直接值为参数
- if (is_array($valueObj["params"]) && count($valueObj["params"]) > 0) {
- // 数组则遍历出来
- foreach ($valueObj["params"] as $paramItem) {
- $desc[$param][] = $paramItem;
- }
- } else {
- $desc[$param][] = $valueObj["params"];
- }
-
- } else if (!empty($valueObj['type']) && $valueObj['type'] === 'tree') {
- // 类型为tree的
- $childrenField = "children";
- if (!empty($valueObj['childrenField'])) {
- $childrenField = $valueObj['childrenField'];
- }
- $childrenDesc = "children";
- if (!empty($valueObj['childrenDesc'])) {
- $childrenDesc = $valueObj['childrenDesc'];
- }
- $valueObj['params'][] = array(
- 'params' => $valueObj['params'],
- 'name' => $childrenField,
- 'type' => 'array',
- 'desc' => $childrenDesc,
- );
- $desc[$param][] = $valueObj;
- } else {
- $desc[$param][] = $valueObj;
- }
-
- } else if ($param == 'return') {
- $valueObj = $this->formatReturn($value);
- if (!empty($valueObj['params']) && empty($valueObj['name'])) {
- // 只配置参数,没配置name则直接值为参数
- if (is_array($valueObj["params"]) && count($valueObj["params"]) > 0) {
- // 数组则遍历出来
- foreach ($valueObj["params"] as $paramItem) {
- $desc[$param][] = $paramItem;
- }
- } else {
- $desc[$param][] = $valueObj["params"];
- }
- } else if (!empty($valueObj['type']) && $valueObj['type'] === 'tree') {
- // 类型为tree的
- $childrenField = "children";
- if (!empty($valueObj['childrenField'])) {
- $childrenField = $valueObj['childrenField'];
- }
- $childrenDesc = "children";
- if (!empty($valueObj['childrenDesc'])) {
- $childrenDesc = $valueObj['childrenDesc'];
- }
- $valueObj['params'][] = array(
- 'params' => $valueObj['params'],
- 'name' => $childrenField,
- 'type' => 'array',
- 'desc' => $childrenDesc,
- );
- $desc[$param][] = $valueObj;
- } else {
- $desc[$param][] = $valueObj;
- }
-
- } else if ($param == 'header') {
- $valueObj = $this->formatHeaders($value);
- $desc[$param][] = $valueObj;
- } else if ($param == 'addField') {
- // 模型指定添加的字段
- $valueObj = $this->formatHeaders($value);
- $desc[$param][] = $valueObj;
- } else {
- $desc[$param] = $value;
- }
-
- }
- }
-
- }
- return $desc;
- }
-
- // 处理Headers的解析
- private function formatHeaders($string)
- {
- $string = $string . " ";
- if (preg_match_all('/(\w+):(.*?)[\s\n]/s', $string, $meatchs)) {
- $param = [];
- foreach ($meatchs[1] as $key => $value) {
- $paramKey = $meatchs[1][$key];
- $value = $meatchs[2][$key];
- $param[$paramKey] = $value;
- }
- return $param;
- } else {
- return '' . $string;
- }
- }
-
- // 处理Param的解析
- private function formatParam($string)
- {
- $string = $string . " ";
- if (preg_match_all('/(\w+):(.*?)[\s\n]/s', $string, $meatchs)) {
- $param = [];
- foreach ($meatchs[1] as $key => $value) {
- $paramKey = $meatchs[1][$key];
- $value = $meatchs[2][$key];
- if ($paramKey == "params") {
- // 处理对象类型
- $value = $this->parseObjectLine($value);
- } else if ($paramKey == "ref") {
- // 处理引用
- $value = $this->parseRefLine($value, "param");
- $paramKey = "params";
- } else if ($paramKey == "field" && !empty($param["params"])) {
- // 只取模型指定字段
- $param["params"] = $this->filterModelTableField($param["params"], $value, "field");
- } else if ($paramKey == "withoutField" && !empty($param["params"])) {
- // 排除模型指定字段
- $param["params"] = $this->filterModelTableField($param["params"], $value, "withoutField");
- }
- $param[$paramKey] = $value;
- }
- return $param;
- } else {
- return '' . $string;
- }
- }
-
- // 处理Return的解析
- private function formatReturn($string)
- {
- $string = $string . " ";
- if (preg_match_all('/(\w+):(.*?)[\s\n]/s', $string, $meatchs)) {
- $param = [];
- foreach ($meatchs[1] as $key => $value) {
- $paramKey = $meatchs[1][$key];
- $value = $meatchs[2][$key];
- if ($paramKey == "params") {
- // 处理对象类型
- $value = $this->parseObjectLine($value);
- } else if ($paramKey == "ref") {
- // 处理引用
- $value = $this->parseRefLine($value, "return");
- if (!empty($value) && is_array($value) && count($value) === 1) {
- if (!empty($value[0]) && !empty($value[0]['params'])) {
- $value = $value[0]['params'];
- }
- }
- $paramKey = "params";
- } else if ($paramKey == "field" && !empty($param["params"])) {
- // 只取模型指定字段
- $param["params"] = $this->filterModelTableField($param["params"], $value, "field");
- } else if ($paramKey == "withoutField" && !empty($param["params"])) {
- // 排除模型指定字段
- $param["params"] = $this->filterModelTableField($param["params"], $value, "withoutField");
- }
- // if ($paramKey == "type" && $value === 'tree') {
- // // 数据结构为树形结构
- // $value =$param;
- // }
- $param[$paramKey] = $value;
- }
- return $param;
- } else {
- return '' . $string;
- }
- }
-
- // 解析param参数为对象类型
- public function parseObjectLine($string)
- {
- $string = trim($string);
- if (empty($string)) {
- return false; // Empty line
- }
- $string = $string . ",";
- if (preg_match_all('/(\w+):(.*?),/s', $string, $meatchs)) {
- $param = [];
- foreach ($meatchs[1] as $key => $value) {
- $paramKey = $meatchs[1][$key];
- $value = $meatchs[2][$key];
- $param[] = array("name" => $paramKey, "type" => $value);
- }
- return $param;
- } else {
- return '' . $string;
- }
-
- }
-
- // 解析ref引用的数据,server、model、引用定义
- public function parseRefLine($string, $paramKey = "")
- {
- $string = trim($string);
- if (empty($string)) {
- return false; // Empty line
- }
- $value = $string;
- if (strpos($string, 'app\\') !== false && strpos($string, 'model\\') === false) {
- // 引用服务
- $value = $this->parseServer($string, $paramKey);
- if (!empty($paramKey) && !empty($value) && !empty($value[$paramKey])) {
- // 存在指定取值的key,去server注释中指定的值
- $value = $value[$paramKey];
- }
-
- } else if (strpos($string, 'model\\') !== false) {
- // 引用模型
- $value = $this->parseModel($string);
- } else if (strpos($string, 'definitions\\') !== false) {
- // 引用定义
- $value = $this->parseDefinitions($string);
- if (!empty($paramKey) && !empty($value) && !empty($value[$paramKey])) {
- // 存在指定取值的key,去server注释中指定的值
- $value = $value[$paramKey];
- }
- }
- return $value;
- }
-
- // 解析服务的注释
- public function parseServer($path, $paramKey)
- {
-
- $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);
- $doc_str = $methodAction->getDocComment();
- $action_doc = $this->parseParam($doc_str);
- return $action_doc;
- }
-
- // 解析模型的注释
- public function parseModel($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);
- //获取模型方法的注释
- $doc_str = $methodAction->getDocComment();
- //解析注释
- $action_doc = $this->parseParam($doc_str);
- // 获取表字段
- $model = $this->getModel($methodAction, $modelClassName);
- $table = $this->getTableDocument($model);
-
- //过滤field
- if (!empty($action_doc) && !empty($action_doc['field'])) {
- $table = $this->filterModelTableField($table, $action_doc['field'], "field");
- } else if (!empty($action_doc) && !empty($action_doc['withoutField'])) {
- $table = $this->filterModelTableField($table, $action_doc['withoutField'], "withoutField");
- }
- if (!empty($action_doc) && !empty($action_doc['addField'])) {
- $table = array_merge($table, $action_doc['addField']);
- }
-
- return $table;
- }
-
- // 获取模型
- private 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;
- }
-
- }
-
- // 过滤模型字段
- public function filterModelTableField($params, $keys, $type = "field")
- {
- $modelParams = [];
- $fieldArr = explode(',', $keys);
- foreach ($params as $modelParam) {
- if (!empty($modelParam['name']) && in_array($modelParam['name'], $fieldArr) && $type == "field") {
- // 取指定字段
- $modelParams[] = $modelParam;
- } else if (!(!empty($modelParam['name']) && in_array($modelParam['name'], $fieldArr)) && $type == "withoutField") {
- // 排除指定字段
- $modelParams[] = $modelParam;
- }
- }
- return $modelParams;
- }
-
- // 解析定义的注释
- public function parseDefinitions($path)
- {
- $modelClassArr = explode("\\", $path);
- $modelActionName = $modelClassArr[count($modelClassArr) - 1];
- $definitionsPath = !empty($this->config['definitions']) ? $this->config['definitions'] : "hg\apidoc\Definitions";
- $classReflect = new \ReflectionClass($definitionsPath);
- $modelActionName = trim($modelActionName);
- $methodAction = $classReflect->getMethod($modelActionName);
- $doc_str = $methodAction->getDocComment();
- $action_doc = $this->parseParam($doc_str);
- return $action_doc;
- }
-
- /**
- * 解析参数的注释,server的注释解析
- * @param string $doc
- * @return array
- */
- public function parseParam($doc = '')
- {
- if ($doc == '') {
- return false;
- }
- // Get the comment
- if (preg_match('#^/\*\*(.*)\*/#s', $doc, $comment) === false) {
- return false;
- }
-
- $comment = trim($comment[1]);
- // Get all the lines and strip the * from the first character
- if (preg_match_all('#^\s*\*(.*)#m', $comment, $lines) === false) {
- return false;
- }
-
- $res = $this->parseActionLines($lines[1]);
-
- return $res;
- }
-
- /**
- * 根据模型获取表的注释
- * @param Model $model
- * @return array
- */
- public function getTableDocument($model)
- {
- $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'];
- }
- preg_match_all("#`(.*?)`(.*?),#", $createSQL, $matches);
- $fields = $matches[1];
- $types = $matches[2];
- $fieldComment = [];
- //组织注释
- for ($i = 0; $i < count($matches[0]); $i++) {
- $key = $fields[$i];
-
- $typeString = $types[$i];
- $typeString = trim($typeString);
- $typeArr = explode(' ', $typeString);
- $type = $typeArr[0];
- $default = "";
- $require = "0";
- $desc = "";
- if (strpos($typeString, 'COMMENT') !== false) {
- // 存在字段注释
- preg_match_all("#COMMENT\s*'(.*?)'#", $typeString, $edscs);
- if (!empty($edscs[1]) && !empty($edscs[1][0])) {
- $desc = $edscs[1][0];
- }
-
- }
- if (strpos($typeString, 'DEFAULT') !== false) {
- // 存在字段默认值
- preg_match_all("#DEFAULT\s*'(.*?)'#", $typeString, $defaults);
- if (!empty($defaults[1]) && !empty($defaults[1][0])) {
- $default = $defaults[1][0];
- }
-
- }
-
- if (strpos($typeString, 'NOT NULL') !== false) {
- // 必填字段
- $require = "1";
- }
-
- $fieldComment[] = [
- "name" => $key,
- "type" => $type,
- "desc" => $desc,
- "default" => $default,
- "require" => $require,
- // "str"=>$createSQL
- ];
- }
- return $fieldComment;
- }
-
- /**
- * 获取类文件的内容
- * @param $className
- * @return mixed
- * @throws \Exception
- */
- protected function getClassFileContent($className)
- {
- if (class_exists($className)) {
- $classReflect = new \ReflectionClass($className);
- } else {
- throw new \Exception("类不存在", '1');
- }
- if (!isset($this->classFileMaps[$className])) {
- $this->classFileMaps[$className] = file_get_contents($classReflect->getFileName());
- }
- return $this->classFileMaps[$className];
- }
-
- public function getIncludeClassName($mainClass, $class)
- {
- $classFile = $this->getClassFileContent($mainClass);
- $pattern = "/use\s*(app.*?\\\\$class)/";
- if (preg_match($pattern, $classFile, $matches)) {
- return $matches[1];
- } else {
- $classReflect = new \ReflectionClass($mainClass);
- $possibleClass = $classReflect->getNamespaceName() . "\\" . $class;
- if (class_exists($possibleClass)) {
- return $possibleClass;
- } else {
- return "";
- }
- }
- }
-
- }
|