| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- <?php
- declare(strict_types = 1);
- namespace hg\apidoc\generator;
- use hg\apidoc\Utils;
- use hg\apidoc\utils\DirAndFile;
- use hg\apidoc\utils\Helper;
- use think\facade\App;
- class ParseTemplate
- {
- public function compile($path,$params)
- {
- $filePath = $path;
- $tplContent = DirAndFile::getFileContent($filePath);
- $tplContent = $this->replaceForeach($tplContent,$params);
- $tplContent = $this->replaceParams($tplContent,$params);
- $tplContent = $this->replaceIf($tplContent,$params);
- $tplContent = preg_replace("/\s+\r\n/is", "\r\n", $tplContent);
- return $tplContent;
- }
- /**
- * 替换变量
- * @param $tplContent
- * @param $params
- * @return array|string|string[]|null
- */
- protected function replaceParams($tplContent,$params){
- $key = '{$%%}';
- $pattern = '#' . str_replace('%%', '(.+?)' , preg_quote($key, '#')) . '#';
- $tplContent = preg_replace_callback($pattern, function ($matches)use ($params){
- $k = $matches[1];
- if (strpos($k, '(') !== false){
- $tagArr = explode("(", $k);
- $fun = $tagArr[0];
- $k = str_replace(")", "",$tagArr[1] );
- $v = $this->getObjectValueByKeys($params,$k);
- if (empty($v)){
- return "";
- }
- if ($fun === "lower"){
- return Helper::lower($v);
- }else if ($fun === "snake"){
- return Helper::snake($v);
- }else if ($fun === "lcfirst"){
- return lcfirst($v);
- }else if ($fun === "ucfirst"){
- return ucfirst($v);
- }else if ($fun === "count"){
- return count($v);
- }
- }
- $value = $this->getObjectValueByKeys($params,$k);
- if (is_bool($value)){
- return $value==true?'true':'false';
- }else if (is_array($value)){
- return $k;
- }
- return $value;
- }, $tplContent);
- return $tplContent;
- }
- /**
- * 替换if内容
- * @param $tplContent
- * @param $params
- * @return array|mixed|string|string[]
- * @throws \Exception
- */
- protected function replaceIf($tplContent,$params){
- $res = [];
- $label = "if";
- $labelList = $this->parseLabel($tplContent,$label);
- if (!empty($labelList) && count($labelList)>0){
- foreach ($labelList as $item) {
- $itemStr =$item;
- $ifChildren= $this->parseLabel($itemStr,$label,"children");
- if (!empty($ifChildren) && count($ifChildren)>0){
- foreach ($ifChildren as $ifChild){
- $itemChildrenContent= $this->getIfContent($ifChild);
- $itemStr = str_replace($ifChild, $itemChildrenContent,$itemStr );
- }
- }
- $itemContent= $this->getIfContent($itemStr);
- $tplContent = str_replace($item, $itemContent,$tplContent );
- }
- }
- return $tplContent;
- }
- protected function parseForeach($str,$params){
- if (preg_match('#{foreach (.+?) as (.+?)=>(.+?)}#s', $str, $matches)){
- $complete = $matches[0];
- $condition = $matches[1];
- $keyField = str_replace("$", "",$matches[2] );
- $itemField = str_replace("$", "",$matches[3] );
- $conditionKey = str_replace("$", "",$condition );
- $forListData = $this->getObjectValueByKeys($params,$conditionKey);
- $contentStr = str_replace($complete, "",$str);
- $contentStr = substr($contentStr,0,-10);
- return [
- 'list'=>$forListData,
- 'keyField'=>$keyField,
- 'itemField'=>$itemField,
- 'content'=>$contentStr
- ];
- }
- return [];
- }
- /**
- * 获取所有foreach标签
- * @param $str
- * @return array
- * @throws \Exception
- */
- protected function getAllForeachLabel($str){
- $tree = [];
- $label = "foreach";
- $labelList = $this->parseLabel($str,$label);
- if (!empty($labelList) && count($labelList)>0){
- foreach ($labelList as $itemLabel) {
- $labelChildrenList = $this->parseLabel($itemLabel,$label,"children");
- if (!empty($labelChildrenList) && count($labelChildrenList)>0){
- $childrenList = [];
- foreach ($labelChildrenList as $item) {
- $childrenList[]=[
- 'str'=>$item,
- 'children' => []
- ];
- }
- $tree[]=[
- 'str'=>$itemLabel,
- 'children' => $childrenList
- ];
- }else{
- $tree[]=[
- 'str'=>$itemLabel,
- 'children' => []
- ];
- }
- }
- }
- return $tree;
- }
- // 解析foreach
- protected function replaceForeach($html,$params,$level=""){
- $allLabelData= $this->getAllForeachLabel($html);
- $res = [];
- if (count($allLabelData)>0){
- // 遍历每个foreach标签
- foreach ($allLabelData as $labelItem) {
- $itemStr = $labelItem['str'];
- $forOption = $this->parseForeach($labelItem['str'],$params);
- $itemContent="";
- if (!empty($forOption['list']) && count($forOption['list'])>0){
- // 处理每行数据
- foreach ($forOption['list'] as $rowKey=>$row) {
- $rowData = [$forOption['itemField']=>$row,$forOption['keyField']=>$rowKey];
- $rowParams = array_merge($params,$rowData);
- // 存在子标签,处理子标签
- if (!empty($labelItem['children']) && count($labelItem['children'])>0){
- $itemStrContent = "";
- foreach ($labelItem['children'] as $childLabel){
- $childContents = "";
- $childStr = $childLabel['str'];
- $childDataList = $this->parseForeach($childLabel['str'],$rowParams);
- // 处理子标签数据
- if (!empty($childDataList['list']) && count($childDataList['list'])>0){
- foreach ($childDataList['list'] as $childDataKey=>$childDataItem) {
- // 子标签每行数据
- $childDataItemData = [$childDataList['itemField']=>$childDataItem,$childDataList['keyField']=>$childDataKey,];
- $contentsStr= $this->getForContent($childDataList['content'],array_merge($rowParams,$childDataItemData));
- $contentsStr =ltrim($contentsStr,"\r\n");
- if (!empty(Helper::trimEmpty($contentsStr))){
- $childContents.= $contentsStr;
- }
- }
- }
- $itemStrContent.= str_replace($childLabel['str'], $childContents,$forOption['content']);
- }
- $rowContent=$this->replaceParams($itemStrContent,$rowParams);
- $itemContentStr=$this->replaceIf($rowContent,$rowParams);
- if (!empty(Helper::trimEmpty($itemContentStr))){
- $itemContent.= $itemContentStr;
- }
- }else{
- $rowContent=$this->getForContent($forOption['content'],$rowParams);
- if (empty(Helper::trimEmpty($rowContent))){
- $rowContent= "";
- }
- $itemContent.= $rowContent;
- }
- $itemContent =trim($itemContent,"\r\n");
- }
- }
- $html = str_replace($labelItem['str'], $itemContent,$html );
- }
- }
- return $html;
- }
- /**
- * 获取foreach内容
- * @param $str
- * @param $params
- * @return array|mixed|string|string[]
- * @throws \Exception
- */
- protected function getForContent($str,$params){
- $content = $str;
- if (!empty($params)){
- $content = $this->replaceParams($content,$params);
- $content = $this->replaceIf($content,$params);
- }
- return $content;
- }
- /**
- * 获取if条件的内容
- * @param $str
- * @return mixed|string
- */
- protected function getIfContent($str){
- if (preg_match('#{if (.+?)}(.*?){/if}#s', $str, $matches)){
- if (eval("return $matches[1];")){
- // 条件成立
- return $matches[2];
- }
- }
- return "";
- }
- /**
- * 解析指定标签
- * @param $str
- * @param $label
- * @param string $level
- * @return array
- * @throws \Exception
- */
- protected function parseLabel($str,$label,$level=""){
- // 后面的 flag 表示记录偏移量
- preg_match_all('!({/?'.$label.' ?}?)!', $str, $matches, PREG_OFFSET_CAPTURE);
- // 用数组来模拟栈
- $stack = [];
- $top = null;
- $result = [];
- foreach ($matches[0] as $k=>[$match, $offset]) {
- // 当取标签内容时,排除第一个和最后一个标签
- if ($level === 'children' && ($k==0 || $k>=count($matches[0])-1)){
- continue;
- }
- // 判断匹配到的如果是 开始标签
- if ($match === '{'.$label.' ') {
- $stack[] = $offset;
- // 记录开始的位置
- if ($top === null) {
- $top = $offset;
- }
- // 如果不是
- } else {
- // 从栈底部拿一个出来
- $pop = array_pop($stack);
- // 如果取出来的是 null 就说明存在多余的 标签
- if ($pop === null) {
- throw new \Exception('语法错误,存在多余的 {/'.$label.'} 标签');
- }
- // 如果取完后栈空了
- if (empty($stack)) {
- // offset 是匹配到的开始标签(前面)位置,加上内容的长度
- $newOffset = $offset + strlen($match)-$top;
- // 从顶部到当前的偏移就是这个标签里的内容
- $result[] = substr($str, $top, $newOffset);
- // 重置 top 开始下一轮
- $top = null;
- }
- }
- }
- // 如果运行完了,栈里面还有东西,那就说明缺少闭合标签。
- if (!empty($stack)) {
- throw new \Exception('语法错误,存在未闭合的 {/'.$label.'} 标签');
- }
- return $result;
- }
- /**
- * 根据keys获取对象中的值
- * @param $array
- * @param $keyStr
- * @param string $delimiter
- * @return mixed|null
- */
- public function getObjectValueByKeys($array, $keyStr, $delimiter = '.')
- {
- $keys = explode($delimiter, $keyStr);
- if (preg_match_all('#\[(.+?)]#s', $keyStr, $matches)){
- $value = $array;
- if (!empty($matches[1])){
- $matchesIndex=0;
- foreach ($keys as $keyItem) {
- if (strpos($keyItem, '[') !== false) {
- $tagArr = explode("[", $keyItem);
- if (!empty($value[$tagArr[0]]) && !empty($value[$tagArr[0]][$matches[1][$matchesIndex]])){
- $value =$value[$tagArr[0]][$matches[1][$matchesIndex]];
- }else{
- $value =null;
- break;
- }
- $matchesIndex=$matchesIndex+1;
- }else{
- $value =$value[$keyItem];
- }
- }
- }
- return $value;
- }else if (sizeof($keys) > 1) {
- $value = $array;
- foreach ($keys as $key){
- if (!empty($value[$key])){
- $value = $value[$key];
- }else{
- $value =null;
- break;
- }
- }
- return $value;
- } else {
- return $array[$keyStr] ?? null;
- }
- }
- }
|