multi-store/vendor/hg/apidoc/src/generator/ParseTemplate.php

341 lines
13 KiB
PHP
Raw Normal View History

2024-06-03 11:03:08 +08:00
<?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;
}
}
}