// +---------------------------------------------------------------------- namespace think\template\taglib; use think\template\TagLib; /** * CX标签库解析类 * @category Think * @package Think * @subpackage Driver.Taglib * @author liu21st */ class Cx extends Taglib { // 标签定义 protected $tags = [ // 标签定义: attr 属性列表 close 是否闭合(0 或者1 默认1) alias 标签别名 level 嵌套层次 'php' => ['attr' => ''], 'volist' => ['attr' => 'name,id,offset,length,key,mod', 'alias' => 'iterate'], 'foreach' => ['attr' => 'name,id,item,key,offset,length,mod', 'expression' => true], 'if' => ['attr' => 'condition', 'expression' => true], 'elseif' => ['attr' => 'condition', 'close' => 0, 'expression' => true], 'else' => ['attr' => '', 'close' => 0], 'switch' => ['attr' => 'name', 'expression' => true], 'case' => ['attr' => 'value,break', 'expression' => true], 'default' => ['attr' => '', 'close' => 0], 'compare' => ['attr' => 'name,value,type', 'alias' => ['eq,equal,notequal,neq,gt,lt,egt,elt,heq,nheq', 'type']], 'range' => ['attr' => 'name,value,type', 'alias' => ['in,notin,between,notbetween', 'type']], 'empty' => ['attr' => 'name'], 'notempty' => ['attr' => 'name'], 'present' => ['attr' => 'name'], 'notpresent' => ['attr' => 'name'], 'defined' => ['attr' => 'name'], 'notdefined' => ['attr' => 'name'], 'load' => ['attr' => 'file,href,type,value,basepath', 'close' => 0, 'alias' => ['import,css,js', 'type']], 'assign' => ['attr' => 'name,value', 'close' => 0], 'define' => ['attr' => 'name,value', 'close' => 0], 'for' => ['attr' => 'start,end,name,comparison,step'], 'url' => ['attr' => 'link,vars,suffix,domain', 'close' => 0, 'expression' => true], 'function' => ['attr' => 'name,vars,use,call'], ]; /** * php标签解析 * 格式: * {php}echo $name{/php} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagPhp($tag, $content) { $parseStr = ''; return $parseStr; } /** * volist标签解析 循环输出数据集 * 格式: * {volist name="userList" id="user" empty=""} * {user.username} * {user.email} * {/volist} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string|void */ public function tagVolist($tag, $content) { $name = $tag['name']; $id = $tag['id']; $empty = isset($tag['empty']) ? $tag['empty'] : ''; $key = !empty($tag['key']) ? $tag['key'] : 'i'; $mod = isset($tag['mod']) ? $tag['mod'] : '2'; $offset = !empty($tag['offset']) && is_numeric($tag['offset']) ? intval($tag['offset']) : 0; $length = !empty($tag['length']) && is_numeric($tag['length']) ? intval($tag['length']) : 'null'; // 允许使用函数设定数据集 {$vo.name} $parseStr = 'autoBuildVar($name); $parseStr .= '$_result=' . $name . ';'; $name = '$_result'; } else { $name = $this->autoBuildVar($name); } $parseStr .= 'if(is_array(' . $name . ') || ' . $name . ' instanceof \think\Collection || ' . $name . ' instanceof \think\Paginator): $' . $key . ' = 0;'; // 设置了输出数组长度 if (0 != $offset || 'null' != $length) { $parseStr .= '$__LIST__ = is_array(' . $name . ') ? array_slice(' . $name . ',' . $offset . ',' . $length . ', true) : ' . $name . '->slice(' . $offset . ',' . $length . ', true); '; } else { $parseStr .= ' $__LIST__ = ' . $name . ';'; } $parseStr .= 'if( count($__LIST__)==0 ) : echo "' . $empty . '" ;'; $parseStr .= 'else: '; $parseStr .= 'foreach($__LIST__ as $key=>$' . $id . '): '; $parseStr .= '$mod = ($' . $key . ' % ' . $mod . ' );'; $parseStr .= '++$' . $key . ';?>'; $parseStr .= $content; $parseStr .= ''; if (!empty($parseStr)) { return $parseStr; } return; } /** * foreach标签解析 循环输出数据集 * 格式: * {foreach name="userList" id="user" key="key" index="i" mod="2" offset="3" length="5" empty=""} * {user.username} * {/foreach} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string|void */ public function tagForeach($tag, $content) { // 直接使用表达式 if (!empty($tag['expression'])) { $expression = ltrim(rtrim($tag['expression'], ')'), '('); $expression = $this->autoBuildVar($expression); $parseStr = ''; $parseStr .= $content; $parseStr .= ''; return $parseStr; } $name = $tag['name']; $key = !empty($tag['key']) ? $tag['key'] : 'key'; $item = !empty($tag['id']) ? $tag['id'] : $tag['item']; $empty = isset($tag['empty']) ? $tag['empty'] : ''; $offset = !empty($tag['offset']) && is_numeric($tag['offset']) ? intval($tag['offset']) : 0; $length = !empty($tag['length']) && is_numeric($tag['length']) ? intval($tag['length']) : 'null'; $parseStr = 'autoBuildVar($name); $parseStr .= $var . '=' . $name . '; '; $name = $var; } else { $name = $this->autoBuildVar($name); } $parseStr .= 'if(is_array(' . $name . ') || ' . $name . ' instanceof \think\Collection || ' . $name . ' instanceof \think\Paginator): '; // 设置了输出数组长度 if (0 != $offset || 'null' != $length) { if (!isset($var)) { $var = '$_' . uniqid(); } $parseStr .= $var . ' = is_array(' . $name . ') ? array_slice(' . $name . ',' . $offset . ',' . $length . ', true) : ' . $name . '->slice(' . $offset . ',' . $length . ', true); '; } else { $var = &$name; } $parseStr .= 'if( count(' . $var . ')==0 ) : echo "' . $empty . '" ;'; $parseStr .= 'else: '; // 设置了索引项 if (isset($tag['index'])) { $index = $tag['index']; $parseStr .= '$' . $index . '=0; '; } $parseStr .= 'foreach(' . $var . ' as $' . $key . '=>$' . $item . '): '; // 设置了索引项 if (isset($tag['index'])) { $index = $tag['index']; if (isset($tag['mod'])) { $mod = (int) $tag['mod']; $parseStr .= '$mod = ($' . $index . ' % ' . $mod . '); '; } $parseStr .= '++$' . $index . '; '; } $parseStr .= '?>'; // 循环体中的内容 $parseStr .= $content; $parseStr .= ''; if (!empty($parseStr)) { return $parseStr; } return; } /** * if标签解析 * 格式: * {if condition=" $a eq 1"} * {elseif condition="$a eq 2" /} * {else /} * {/if} * 表达式支持 eq neq gt egt lt elt == > >= < <= or and || && * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagIf($tag, $content) { $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition']; $condition = $this->parseCondition($condition); $parseStr = '' . $content . ''; return $parseStr; } /** * elseif标签解析 * 格式:见if标签 * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagElseif($tag, $content) { $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition']; $condition = $this->parseCondition($condition); $parseStr = ''; return $parseStr; } /** * else标签解析 * 格式:见if标签 * @access public * @param array $tag 标签属性 * @return string */ public function tagElse($tag) { $parseStr = ''; return $parseStr; } /** * switch标签解析 * 格式: * {switch name="a.name"} * {case value="1" break="false"}1{/case} * {case value="2" }2{/case} * {default /}other * {/switch} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagSwitch($tag, $content) { $name = !empty($tag['expression']) ? $tag['expression'] : $tag['name']; $name = $this->autoBuildVar($name); $parseStr = '' . $content . ''; return $parseStr; } /** * case标签解析 需要配合switch才有效 * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagCase($tag, $content) { $value = isset($tag['expression']) ? $tag['expression'] : $tag['value']; $flag = substr($value, 0, 1); if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($value); $value = 'case ' . $value . ':'; } elseif (strpos($value, '|')) { $values = explode('|', $value); $value = ''; foreach ($values as $val) { $value .= 'case "' . addslashes($val) . '":'; } } else { $value = 'case "' . $value . '":'; } $parseStr = '' . $content; $isBreak = isset($tag['break']) ? $tag['break'] : ''; if ('' == $isBreak || $isBreak) { $parseStr .= ''; } return $parseStr; } /** * default标签解析 需要配合switch才有效 * 使用: {default /}ddfdf * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagDefault($tag) { $parseStr = ''; return $parseStr; } /** * compare标签解析 * 用于值的比较 支持 eq neq gt lt egt elt heq nheq 默认是eq * 格式: {compare name="" type="eq" value="" }content{/compare} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagCompare($tag, $content) { $name = $tag['name']; $value = $tag['value']; $type = isset($tag['type']) ? $tag['type'] : 'eq'; // 比较类型 $name = $this->autoBuildVar($name); $flag = substr($value, 0, 1); if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($value); } else { $value = '\'' . $value . '\''; } switch ($type) { case 'equal': $type = 'eq'; break; case 'notequal': $type = 'neq'; break; } $type = $this->parseCondition(' ' . $type . ' '); $parseStr = '' . $content . ''; return $parseStr; } /** * range标签解析 * 如果某个变量存在于某个范围 则输出内容 type= in 表示在范围内 否则表示在范围外 * 格式: {range name="var|function" value="val" type='in|notin' }content{/range} * example: {range name="a" value="1,2,3" type='in' }content{/range} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagRange($tag, $content) { $name = $tag['name']; $value = $tag['value']; $type = isset($tag['type']) ? $tag['type'] : 'in'; // 比较类型 $name = $this->autoBuildVar($name); $flag = substr($value, 0, 1); if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($value); $str = 'is_array(' . $value . ')?' . $value . ':explode(\',\',' . $value . ')'; } else { $value = '"' . $value . '"'; $str = 'explode(\',\',' . $value . ')'; } if ('between' == $type) { $parseStr = '= $_RANGE_VAR_[0] && ' . $name . '<= $_RANGE_VAR_[1]):?>' . $content . ''; } elseif ('notbetween' == $type) { $parseStr = '$_RANGE_VAR_[1]):?>' . $content . ''; } else { $fun = ('in' == $type) ? 'in_array' : '!in_array'; $parseStr = '' . $content . ''; } return $parseStr; } /** * present标签解析 * 如果某个变量已经设置 则输出内容 * 格式: {present name="" }content{/present} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagPresent($tag, $content) { $name = $tag['name']; $name = $this->autoBuildVar($name); $parseStr = '' . $content . ''; return $parseStr; } /** * notpresent标签解析 * 如果某个变量没有设置,则输出内容 * 格式: {notpresent name="" }content{/notpresent} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagNotpresent($tag, $content) { $name = $tag['name']; $name = $this->autoBuildVar($name); $parseStr = '' . $content . ''; return $parseStr; } /** * empty标签解析 * 如果某个变量为empty 则输出内容 * 格式: {empty name="" }content{/empty} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagEmpty($tag, $content) { $name = $tag['name']; $name = $this->autoBuildVar($name); $parseStr = 'isEmpty())): ?>' . $content . ''; return $parseStr; } /** * notempty标签解析 * 如果某个变量不为empty 则输出内容 * 格式: {notempty name="" }content{/notempty} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagNotempty($tag, $content) { $name = $tag['name']; $name = $this->autoBuildVar($name); $parseStr = 'isEmpty()))): ?>' . $content . ''; return $parseStr; } /** * 判断是否已经定义了该常量 * {defined name='TXT'}已定义{/defined} * @access public * @param array $tag * @param string $content * @return string */ public function tagDefined($tag, $content) { $name = $tag['name']; $parseStr = '' . $content . ''; return $parseStr; } /** * 判断是否没有定义了该常量 * {notdefined name='TXT'}已定义{/notdefined} * @access public * @param array $tag * @param string $content * @return string */ public function tagNotdefined($tag, $content) { $name = $tag['name']; $parseStr = '' . $content . ''; return $parseStr; } /** * load 标签解析 {load file="/static/js/base.js" /} * 格式:{load file="/static/css/base.css" /} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagLoad($tag, $content) { $file = isset($tag['file']) ? $tag['file'] : $tag['href']; $type = isset($tag['type']) ? strtolower($tag['type']) : ''; $parseStr = ''; $endStr = ''; // 判断是否存在加载条件 允许使用函数判断(默认为isset) if (isset($tag['value'])) { $name = $tag['value']; $name = $this->autoBuildVar($name); $name = 'isset(' . $name . ')'; $parseStr .= ''; $endStr = ''; } // 文件方式导入 $array = explode(',', $file); foreach ($array as $val) { $type = strtolower(substr(strrchr($val, '.'), 1)); switch ($type) { case 'js': $parseStr .= ''; break; case 'css': $parseStr .= ''; break; case 'php': $parseStr .= ''; break; } } return $parseStr . $endStr; } /** * assign标签解析 * 在模板中给某个变量赋值 支持变量赋值 * 格式: {assign name="" value="" /} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagAssign($tag, $content) { $name = $this->autoBuildVar($tag['name']); $flag = substr($tag['value'], 0, 1); if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($tag['value']); } else { $value = '\'' . $tag['value'] . '\''; } $parseStr = ''; return $parseStr; } /** * define标签解析 * 在模板中定义常量 支持变量赋值 * 格式: {define name="" value="" /} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagDefine($tag, $content) { $name = '\'' . $tag['name'] . '\''; $flag = substr($tag['value'], 0, 1); if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($tag['value']); } else { $value = '\'' . $tag['value'] . '\''; } $parseStr = ''; return $parseStr; } /** * for标签解析 * 格式: * {for start="" end="" comparison="" step="" name=""} * content * {/for} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagFor($tag, $content) { //设置默认值 $start = 0; $end = 0; $step = 1; $comparison = 'lt'; $name = 'i'; $rand = rand(); //添加随机数,防止嵌套变量冲突 //获取属性 foreach ($tag as $key => $value) { $value = trim($value); $flag = substr($value, 0, 1); if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($value); } switch ($key) { case 'start': $start = $value; break; case 'end': $end = $value; break; case 'step': $step = $value; break; case 'comparison': $comparison = $value; break; case 'name': $name = $value; break; } } $parseStr = 'parseCondition('$' . $name . ' ' . $comparison . ' $__FOR_END_' . $rand . '__') . ';$' . $name . '+=' . $step . '){ ?>'; $parseStr .= $content; $parseStr .= ''; return $parseStr; } /** * url函数的tag标签 * 格式:{url link="模块/控制器/方法" vars="参数" suffix="true或者false 是否带有后缀" domain="true或者false 是否携带域名" /} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagUrl($tag, $content) { $url = isset($tag['link']) ? $tag['link'] : ''; $vars = isset($tag['vars']) ? $tag['vars'] : ''; $suffix = isset($tag['suffix']) ? $tag['suffix'] : 'true'; $domain = isset($tag['domain']) ? $tag['domain'] : 'false'; return ''; } /** * function标签解析 匿名函数,可实现递归 * 使用: * {function name="func" vars="$data" call="$list" use="&$a,&$b"} * {if is_array($data)} * {foreach $data as $val} * {~func($val) /} * {/foreach} * {else /} * {$data} * {/if} * {/function} * @access public * @param array $tag 标签属性 * @param string $content 标签内容 * @return string */ public function tagFunction($tag, $content) { $name = !empty($tag['name']) ? $tag['name'] : 'func'; $vars = !empty($tag['vars']) ? $tag['vars'] : ''; $call = !empty($tag['call']) ? $tag['call'] : ''; $use = ['&$' . $name]; if (!empty($tag['use'])) { foreach (explode(',', $tag['use']) as $val) { $use[] = '&' . ltrim(trim($val), '&'); } } $parseStr = '' . $content . '' : '?>'; return $parseStr; } }