由于工作的需要,用到了开源微博系统 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
即可。整个改造过程完毕!!!