ParseTemplate.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. <?php
  2. declare(strict_types = 1);
  3. namespace hg\apidoc\generator;
  4. use hg\apidoc\Utils;
  5. use hg\apidoc\utils\DirAndFile;
  6. use hg\apidoc\utils\Helper;
  7. use think\facade\App;
  8. class ParseTemplate
  9. {
  10. public function compile($path,$params)
  11. {
  12. $filePath = $path;
  13. $tplContent = DirAndFile::getFileContent($filePath);
  14. $tplContent = $this->replaceForeach($tplContent,$params);
  15. $tplContent = $this->replaceParams($tplContent,$params);
  16. $tplContent = $this->replaceIf($tplContent,$params);
  17. $tplContent = preg_replace("/\s+\r\n/is", "\r\n", $tplContent);
  18. return $tplContent;
  19. }
  20. /**
  21. * 替换变量
  22. * @param $tplContent
  23. * @param $params
  24. * @return array|string|string[]|null
  25. */
  26. protected function replaceParams($tplContent,$params){
  27. $key = '{$%%}';
  28. $pattern = '#' . str_replace('%%', '(.+?)' , preg_quote($key, '#')) . '#';
  29. $tplContent = preg_replace_callback($pattern, function ($matches)use ($params){
  30. $k = $matches[1];
  31. if (strpos($k, '(') !== false){
  32. $tagArr = explode("(", $k);
  33. $fun = $tagArr[0];
  34. $k = str_replace(")", "",$tagArr[1] );
  35. $v = $this->getObjectValueByKeys($params,$k);
  36. if (empty($v)){
  37. return "";
  38. }
  39. if ($fun === "lower"){
  40. return Helper::lower($v);
  41. }else if ($fun === "snake"){
  42. return Helper::snake($v);
  43. }else if ($fun === "lcfirst"){
  44. return lcfirst($v);
  45. }else if ($fun === "ucfirst"){
  46. return ucfirst($v);
  47. }else if ($fun === "count"){
  48. return count($v);
  49. }
  50. }
  51. $value = $this->getObjectValueByKeys($params,$k);
  52. if (is_bool($value)){
  53. return $value==true?'true':'false';
  54. }else if (is_array($value)){
  55. return $k;
  56. }
  57. return $value;
  58. }, $tplContent);
  59. return $tplContent;
  60. }
  61. /**
  62. * 替换if内容
  63. * @param $tplContent
  64. * @param $params
  65. * @return array|mixed|string|string[]
  66. * @throws \Exception
  67. */
  68. protected function replaceIf($tplContent,$params){
  69. $res = [];
  70. $label = "if";
  71. $labelList = $this->parseLabel($tplContent,$label);
  72. if (!empty($labelList) && count($labelList)>0){
  73. foreach ($labelList as $item) {
  74. $itemStr =$item;
  75. $ifChildren= $this->parseLabel($itemStr,$label,"children");
  76. if (!empty($ifChildren) && count($ifChildren)>0){
  77. foreach ($ifChildren as $ifChild){
  78. $itemChildrenContent= $this->getIfContent($ifChild);
  79. $itemStr = str_replace($ifChild, $itemChildrenContent,$itemStr );
  80. }
  81. }
  82. $itemContent= $this->getIfContent($itemStr);
  83. $tplContent = str_replace($item, $itemContent,$tplContent );
  84. }
  85. }
  86. return $tplContent;
  87. }
  88. protected function parseForeach($str,$params){
  89. if (preg_match('#{foreach (.+?) as (.+?)=>(.+?)}#s', $str, $matches)){
  90. $complete = $matches[0];
  91. $condition = $matches[1];
  92. $keyField = str_replace("$", "",$matches[2] );
  93. $itemField = str_replace("$", "",$matches[3] );
  94. $conditionKey = str_replace("$", "",$condition );
  95. $forListData = $this->getObjectValueByKeys($params,$conditionKey);
  96. $contentStr = str_replace($complete, "",$str);
  97. $contentStr = substr($contentStr,0,-10);
  98. return [
  99. 'list'=>$forListData,
  100. 'keyField'=>$keyField,
  101. 'itemField'=>$itemField,
  102. 'content'=>$contentStr
  103. ];
  104. }
  105. return [];
  106. }
  107. /**
  108. * 获取所有foreach标签
  109. * @param $str
  110. * @return array
  111. * @throws \Exception
  112. */
  113. protected function getAllForeachLabel($str){
  114. $tree = [];
  115. $label = "foreach";
  116. $labelList = $this->parseLabel($str,$label);
  117. if (!empty($labelList) && count($labelList)>0){
  118. foreach ($labelList as $itemLabel) {
  119. $labelChildrenList = $this->parseLabel($itemLabel,$label,"children");
  120. if (!empty($labelChildrenList) && count($labelChildrenList)>0){
  121. $childrenList = [];
  122. foreach ($labelChildrenList as $item) {
  123. $childrenList[]=[
  124. 'str'=>$item,
  125. 'children' => []
  126. ];
  127. }
  128. $tree[]=[
  129. 'str'=>$itemLabel,
  130. 'children' => $childrenList
  131. ];
  132. }else{
  133. $tree[]=[
  134. 'str'=>$itemLabel,
  135. 'children' => []
  136. ];
  137. }
  138. }
  139. }
  140. return $tree;
  141. }
  142. // 解析foreach
  143. protected function replaceForeach($html,$params,$level=""){
  144. $allLabelData= $this->getAllForeachLabel($html);
  145. $res = [];
  146. if (count($allLabelData)>0){
  147. // 遍历每个foreach标签
  148. foreach ($allLabelData as $labelItem) {
  149. $itemStr = $labelItem['str'];
  150. $forOption = $this->parseForeach($labelItem['str'],$params);
  151. $itemContent="";
  152. if (!empty($forOption['list']) && count($forOption['list'])>0){
  153. // 处理每行数据
  154. foreach ($forOption['list'] as $rowKey=>$row) {
  155. $rowData = [$forOption['itemField']=>$row,$forOption['keyField']=>$rowKey];
  156. $rowParams = array_merge($params,$rowData);
  157. // 存在子标签,处理子标签
  158. if (!empty($labelItem['children']) && count($labelItem['children'])>0){
  159. $itemStrContent = "";
  160. foreach ($labelItem['children'] as $childLabel){
  161. $childContents = "";
  162. $childStr = $childLabel['str'];
  163. $childDataList = $this->parseForeach($childLabel['str'],$rowParams);
  164. // 处理子标签数据
  165. if (!empty($childDataList['list']) && count($childDataList['list'])>0){
  166. foreach ($childDataList['list'] as $childDataKey=>$childDataItem) {
  167. // 子标签每行数据
  168. $childDataItemData = [$childDataList['itemField']=>$childDataItem,$childDataList['keyField']=>$childDataKey,];
  169. $contentsStr= $this->getForContent($childDataList['content'],array_merge($rowParams,$childDataItemData));
  170. $contentsStr =ltrim($contentsStr,"\r\n");
  171. if (!empty(Helper::trimEmpty($contentsStr))){
  172. $childContents.= $contentsStr;
  173. }
  174. }
  175. }
  176. $itemStrContent.= str_replace($childLabel['str'], $childContents,$forOption['content']);
  177. }
  178. $rowContent=$this->replaceParams($itemStrContent,$rowParams);
  179. $itemContentStr=$this->replaceIf($rowContent,$rowParams);
  180. if (!empty(Helper::trimEmpty($itemContentStr))){
  181. $itemContent.= $itemContentStr;
  182. }
  183. }else{
  184. $rowContent=$this->getForContent($forOption['content'],$rowParams);
  185. if (empty(Helper::trimEmpty($rowContent))){
  186. $rowContent= "";
  187. }
  188. $itemContent.= $rowContent;
  189. }
  190. $itemContent =trim($itemContent,"\r\n");
  191. }
  192. }
  193. $html = str_replace($labelItem['str'], $itemContent,$html );
  194. }
  195. }
  196. return $html;
  197. }
  198. /**
  199. * 获取foreach内容
  200. * @param $str
  201. * @param $params
  202. * @return array|mixed|string|string[]
  203. * @throws \Exception
  204. */
  205. protected function getForContent($str,$params){
  206. $content = $str;
  207. if (!empty($params)){
  208. $content = $this->replaceParams($content,$params);
  209. $content = $this->replaceIf($content,$params);
  210. }
  211. return $content;
  212. }
  213. /**
  214. * 获取if条件的内容
  215. * @param $str
  216. * @return mixed|string
  217. */
  218. protected function getIfContent($str){
  219. if (preg_match('#{if (.+?)}(.*?){/if}#s', $str, $matches)){
  220. if (eval("return $matches[1];")){
  221. // 条件成立
  222. return $matches[2];
  223. }
  224. }
  225. return "";
  226. }
  227. /**
  228. * 解析指定标签
  229. * @param $str
  230. * @param $label
  231. * @param string $level
  232. * @return array
  233. * @throws \Exception
  234. */
  235. protected function parseLabel($str,$label,$level=""){
  236. // 后面的 flag 表示记录偏移量
  237. preg_match_all('!({/?'.$label.' ?}?)!', $str, $matches, PREG_OFFSET_CAPTURE);
  238. // 用数组来模拟栈
  239. $stack = [];
  240. $top = null;
  241. $result = [];
  242. foreach ($matches[0] as $k=>[$match, $offset]) {
  243. // 当取标签内容时,排除第一个和最后一个标签
  244. if ($level === 'children' && ($k==0 || $k>=count($matches[0])-1)){
  245. continue;
  246. }
  247. // 判断匹配到的如果是 开始标签
  248. if ($match === '{'.$label.' ') {
  249. $stack[] = $offset;
  250. // 记录开始的位置
  251. if ($top === null) {
  252. $top = $offset;
  253. }
  254. // 如果不是
  255. } else {
  256. // 从栈底部拿一个出来
  257. $pop = array_pop($stack);
  258. // 如果取出来的是 null 就说明存在多余的 标签
  259. if ($pop === null) {
  260. throw new \Exception('语法错误,存在多余的 {/'.$label.'} 标签');
  261. }
  262. // 如果取完后栈空了
  263. if (empty($stack)) {
  264. // offset 是匹配到的开始标签(前面)位置,加上内容的长度
  265. $newOffset = $offset + strlen($match)-$top;
  266. // 从顶部到当前的偏移就是这个标签里的内容
  267. $result[] = substr($str, $top, $newOffset);
  268. // 重置 top 开始下一轮
  269. $top = null;
  270. }
  271. }
  272. }
  273. // 如果运行完了,栈里面还有东西,那就说明缺少闭合标签。
  274. if (!empty($stack)) {
  275. throw new \Exception('语法错误,存在未闭合的 {/'.$label.'} 标签');
  276. }
  277. return $result;
  278. }
  279. /**
  280. * 根据keys获取对象中的值
  281. * @param $array
  282. * @param $keyStr
  283. * @param string $delimiter
  284. * @return mixed|null
  285. */
  286. public function getObjectValueByKeys($array, $keyStr, $delimiter = '.')
  287. {
  288. $keys = explode($delimiter, $keyStr);
  289. if (preg_match_all('#\[(.+?)]#s', $keyStr, $matches)){
  290. $value = $array;
  291. if (!empty($matches[1])){
  292. $matchesIndex=0;
  293. foreach ($keys as $keyItem) {
  294. if (strpos($keyItem, '[') !== false) {
  295. $tagArr = explode("[", $keyItem);
  296. if (!empty($value[$tagArr[0]]) && !empty($value[$tagArr[0]][$matches[1][$matchesIndex]])){
  297. $value =$value[$tagArr[0]][$matches[1][$matchesIndex]];
  298. }else{
  299. $value =null;
  300. break;
  301. }
  302. $matchesIndex=$matchesIndex+1;
  303. }else{
  304. $value =$value[$keyItem];
  305. }
  306. }
  307. }
  308. return $value;
  309. }else if (sizeof($keys) > 1) {
  310. $value = $array;
  311. foreach ($keys as $key){
  312. if (!empty($value[$key])){
  313. $value = $value[$key];
  314. }else{
  315. $value =null;
  316. break;
  317. }
  318. }
  319. return $value;
  320. } else {
  321. return $array[$keyStr] ?? null;
  322. }
  323. }
  324. }