由于工作的需要,用到了开源微博系统 ThinkSNS3.X ,它采用的是 ThinkPHP2.X 框架,当然也做了些修改,在二次开发过程中,感觉 ThinkSNS 的模板语法还是偏复杂,不够精简,最好能精简到连 美工/前端工程师 一眼就能看懂是怎么回事的程度,于是想到了Discuz模板语法,觉得它的语法是我想要的。按照它的语法思路自己写了个模板引擎,同时又不能对 ThinkSNS 自带的模板引擎产生干扰,即要做好兼容工作。
整体思路如下:
1、在 ThinkSNS 自带模板引擎对模板编译之前,先把 “新的模板语法标签”替换成一些“特殊的字符串”,即占位符,如:
把 <!--{if xxx}--> 替换成 <!--IF_TAG_3-->
把 <!--{elseif xxx}--> 替换成 <!--ELSEIF_TAG_4-->
新产生的占位符是不会干扰旧模板引擎的编译的。
2、再把替换后的模板内容传给“旧模板引擎”去编译;
3、再把编译后的模板内容传给“新模板引擎”去编译,替换在第一步中产生的“特殊的字符串”,完成最终的编译过程;
新模板引擎语法(兼容ThinkSNS自带的模板引擎)
1、模板嵌套语法
<!--{template bolglist}--> // 包含 当前应用模块下的 bloglist.html
<!--{template /public/default/blog/hearder}--> // 包含 当前应用下的 header.html·
<!--{template /public/default/footer}--> // 包含 当前主题下的 footer.html
2、逻辑判断 if...else
<!--{if $_G['uid']}-->
任意html语句
<!--{/if}-->
<!--{if $_G['uid']}-->
任意html语句
<!--{else}-->
任意html语句
<!--{/if}-->
<!--{if $_G['uid']}-->
任意 html 语句
<!--{elseif $_G[connectguest]}-->
任意html语句
<!--{/if}-->
<!--{if empty($_G['forum']['picstyle']) && $_GET['orderby'] == 'lastpost' && empty($_GET['filter']) }-->
任意 html 语句
<!--{elseif 条件2}-->
任意html语句
<!--{elseif 条件3}-->
任意html语句
<!--{else}-->
任意html语句
<!--{/if}-->
3、循环语法(可以多重循环)
带有数组键的循环写法
<!--{loop $arr $key $val}-->
循环输出的HTML语句
<!--{/loop}-->
没有数组键的循环写法
<!--{loop $arr $val}-->
循环输出的HTML语句
<!--{/loop}-->
4、输出某个函数的结果
<!--{call widget('comment', array('xxx', 'yyy'))}-->
<!--{call get_user_name(59)}-->
5、直接执行 PHP 代码行
<!--{eval echo $my_var;}-->
<!--{eval $my_arr = array(1, 2, 3);}-->
<!--{eval print_r($my_arr);}-->
<!--{eval output();}-->
<!--{eval exit();}-->
6、直接执行 PHP 代码块
<!--{eval}-->
PHP代码块
<!--{/eval}-->
7、输出变量
<!--{$var}--> 或 {$var}
8、输出常量
<!--{CONST}-->
第一步:把 TemplateNew.class.php 拷贝到 /core/OpenSociax/ 目录下
新模板引擎类(TemplateNew.class.php),内容如下:
<?php /** * 新模板解析类(兼容ThinkSNS自带的模板引擎) * * @author snsgou.com */ class TemplateNew { protected $index = 0; protected $search = array(); protected $replace = array(); /** * 编译模板(查找) * * @param string $template 模板内容 */ public function compileSearch($template) { // 子模板 for ($i = 1; $i < 3; $i++) { $template = preg_replace("/\<\!\-\-\{template\s+(.+?)\}\-\-\>/e", "\$this->readSubTpl('\\1')", $template); } // call标签(输出某个函数的结果) $template = preg_replace("/\<\!\-\-\{call\s+(.+?)\}\-\-\>/e", "\$this->callTag('\\1')", $template); // PHP代码块 $template = preg_replace("/\<\!\-\-\{eval\}\-\-\>(.+?)\<\!\-\-\{\/eval\}\-\-\>/se", "\$this->evalTag('\\1')", $template); $template = preg_replace("/\<\!\-\-\{eval\s+(.+?)\}\-\-\>/e", "\$this->evalTag(' \\1; ')", $template); // 逻辑 $template = preg_replace("/\<\!\-\-\{if\s+(.+?)\}\-\-\>/e", "\$this->ifTag('\\1')", $template); $template = preg_replace("/\<\!\-\-\{elseif\s+(.+?)\}\-\-\>/e", "\$this->elseifTag('\\1')", $template); $template = preg_replace("/\<\!\-\-\{else\}\-\-\>/e", "\$this->elseTag()", $template); $template = preg_replace("/\<\!\-\-\{\/if\}\-\-\>/e", "\$this->endifTag()", $template); // 循环 $template = preg_replace("/\<\!\-\-\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}\-\-\>/e", "\$this->loopkeyvalueTag('\\1', '\\2', '\\3')", $template); $template = preg_replace("/\<\!\-\-\{loop\s+(\S+)\s+(\S+)\}\-\-\>/e", "\$this->loopvalueTag('\\1', '\\2')", $template); $template = preg_replace("/\<\!\-\-\{\/loop\}\-\-\>/e", "\$this->endloopTag()", $template); // 变量 $varRegexp = "((\\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)(\[[a-zA-Z0-9_\-\.\"\'\[\]\$\x7f-\xff]+\])*)"; $template = preg_replace("/\<\!\-\-\{$varRegexp\}\-\-\>/e", "\$this->varTag('\\1')", $template); //$template = preg_replace("/(?<!\<\!\-\-)\{$varRegexp\}(?!\-\-\>)/e", "\$this->varTag('\\1')", $template); // 常量 $constRegexp = "([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)"; $template = preg_replace("/\<\!\-\-\{$constRegexp\}\-\-\>/e", "\$this->constTag('\\1')", $template); //$template = preg_replace("/(?<!\<\!\-\-)\{$constRegexp\}(?!\-\-\>)/e", "\$this->constTag('\\1')", $template); return $template; } /** * 编译模板(替换) * * @param string $template 模板内容 */ public function compileReplace($template) { if (!empty($this->search)) { $template = str_replace($this->search, $this->replace, $template); } return $template; } /** * 读取子模板 */ protected function readSubTpl($tplFile) { $tplFile = $this->getTplPath($tplFile); if (!file_exists($tplFile)) { die('Template file : ' . str_replace(SITE_PATH, '', $tplFile) . ' not found!'); } $content = file_get_contents($tplFile); if (empty($content)) { die('Template file : ' . str_replace(SITE_PATH, '', $tplFile) . ' have no access!'); } return $content; } /** * 获取模板路径(绝对物理路径) */ protected function getTplPath($tplFile) { if (preg_match("/\.html$/i", $tplFile)) { return $tplFile; } elseif (empty($tplFile)) { $tplFile = APP_TPL_PATH . '/' . MODULE_NAME . '/' . ACTION_NAME . '.html'; } elseif (preg_match("/(/public/default|/public/default/blog)/", $tplFile)) { // 路径魔术变量替换 $replace = array( '/public/default' => THEME_PATH, '/public/default/blog' => APP_TPL_PATH ); $tplFile = str_replace(array_keys($replace), array_values($replace), $tplFile) . '.html'; } else { $tplFile = APP_TPL_PATH . '/' . MODULE_NAME . '/' . $tplFile . '.html'; } return $tplFile; } /** * call标签 */ protected function callTag($str) { $search = '<!--CALL_TAG_' . $this->index . '-->'; $this->index++; $this->search[$this->index] = $search; $this->replace[$this->index] = "<?php echo $str; ?>"; return $search; } /** * eval标签 */ protected function evalTag($str) { $search = '<!--EVAL_TAG_' . $this->index . '-->'; $this->index++; $this->search[$this->index] = $search; $this->replace[$this->index] = "<?php$str?>"; return $search; } /** * if标签 */ protected function ifTag($str) { $search = '<!--IF_TAG_' . $this->index . '-->'; $this->index++; $this->search[$this->index] = $search; $this->replace[$this->index] = "<?php if ($str) { ?>"; return $search; } /** * elseif标签 */ protected function elseifTag($str) { $search = '<!--ELSEIF_TAG_' . $this->index . '-->'; $this->index++; $this->search[$this->index] = $search; $this->replace[$this->index] = "<?php } elseif ($str) { ?>"; return $search; } /** * else标签 */ protected function elseTag() { $search = '<!--ELSE_TAG_' . $this->index . '-->'; $this->index++; $this->search[$this->index] = $search; $this->replace[$this->index] = "<?php } else { ?>"; return $search; } /** * endif标签 */ protected function endifTag() { $search = '<!--ENDIF_TAG_' . $this->index . '-->'; $this->index++; $this->search[$this->index] = $search; $this->replace[$this->index] = "<?php } ?>"; return $search; } /** * loop key value 标签 */ protected function loopkeyvalueTag($arr, $key, $value) { $search = '<!--LOOPKEYVALUE_TAG_' . $this->index . '-->'; $this->index++; $this->search[$this->index] = $search; $this->replace[$this->index] = "<?php if (is_array($arr)) { foreach ($arr as $key=>$value) { ?>"; return $search; } /** * loop value 标签 */ protected function loopvalueTag($arr, $value) { $search = '<!--LOOPVALUE_TAG_' . $this->index . '-->'; $this->index++; $this->search[$this->index] = $search; $this->replace[$this->index] = "<?php if (is_array($arr)) { foreach ($arr as $value) { ?>"; return $search; } /** * endloop标签 */ protected function endloopTag() { $search = '<!--ENDLOOP_TAG_' . $this->index . '-->'; $this->index++; $this->search[$this->index] = $search; $this->replace[$this->index] = "<?php }} ?>"; return $search; } /** * var标签 */ protected function varTag($str) { $search = '<!--VAR_TAG_' . $this->index . '-->'; $this->index++; $this->search[$this->index] = $search; $this->replace[$this->index] = "<?php echo $str; ?>"; return $search; } /** * const标签 */ protected function constTag($str) { $search = '<!--CONST_TAG_' . $this->index . '-->'; $this->index++; $this->search[$this->index] = $search; $this->replace[$this->index] = "<?php echo $str; ?>"; return $search; } } ?>
第二步:找到 ThinkSNS 的模板引擎文件 /core/OpenSociax/Template.class.php
把 “public function loadTemplate($tmplTemplateFile = '') {...}”方法中的
// 需要更新模版 读出原模板内容 $tmplContent = file_get_contents($tmplTemplateFile); //编译模板内容 $tmplContent = $this->compiler($tmplContent);
替换成
// 新模板解析类(兼容TS自带的模板引擎) // by snsgou.com tsload(CORE_LIB_PATH . '/TemplateNew.class.php'); $tplNew = new TemplateNew(); // 需要更新模版 读出原模板内容 $tmplContent = file_get_contents($tmplTemplateFile); // 编译模板内容 $tmplContent = $tplNew->compileSearch($tmplContent); // by snsgou.com $tmplContent = $this->compiler($tmplContent); $tmplContent = $tplNew->compileReplace($tmplContent); // by snsgou.com
即可。整个改造过程完毕!!!