刚开始学PHP的时候,对模板解析实在是觉得很奇怪,不知道这个原理怎么实现的,后来看书看多了也明白有一个著名的Smarty在那,曾经也用过一段,不过感觉不是很好,就开始分析Discuz的模板技术是怎么实现的了,然后我把这个模板解析的代码分离出来了,觉得很好用,用了一段时间, Discuz的模板解析是用正则表达式替换一些模板中的规定的语言标记,然后呢,写到forumdata/templates中,再用include引用到index, forumdisplay等等中,和smarty的原理基本上相同,只是功能没有smarty那么多,smarty内建了一个cache来着…连个User Guide都几百页…
呵呵,不过现在基本上两个都没用过,正则表达式好是好用,不过正则的效率不是很高,以前看PHP技术文档的时候说能不用正则就尽量不要用,那东西烦着,现在做什么项目基本上用的是框架,MVC的模式,View中的代码一般不用模板解析出来,混杂php代码在里面,也觉得不错,OOP的开发效率比较高,很多地方重用代码是很开心的~!
Discuz的模板解析要分析出来只要用到两个文件:./include/global.func.php和. /include/template.func.php,global只要一个函数就够了,template要全部的文件下面我就分开讲一下,会比较详细,大家耐心看:
Section One–./include/global.func.php—->template function
1 | <p> function template( $file , $templateid = 0, $tpldir = '' ) {</p> |
2 | <p> global $tplrefresh ; </p><p> $tpldir = $tpldir ? $tpldir : TPLDIR;</p><p> $templateid = $templateid ? $templateid : TEMPLATEID;</p><p> $tplfile = DISCUZ_ROOT. './' . $tpldir . '/' . $file . '.htm' ;</p><p> $objfile = DISCUZ_ROOT. './forumdata/templates/' . $templateid . '_' . $file . '.tpl.php' ; </p><p> if (TEMPLATEID != 1 && $templateid != 1 && ! file_exists ( $tplfile )) { </p><p> return template( $file , 1, './templates/default/' ); </p><p> } </p><p> if ( $tplrefresh == 1 || ( $tplrefresh > 1 && substr ( $GLOBALS [ 'timestamp' ], -1) > $tplrefresh )) {</p> |
3 | if (@ filemtime ( $tplfile ) > @ filemtime ( $objfile )) { |
4 | <p> require_once DISCUZ_ROOT. './include/template.func.php' ;</p> parse_template( $file , $templateid , $tpldir ); |
6 | <p> } </p><p> return $objfile ;</p> |
}
复制代码
这个函数一共有三个传入参数:
$file 表示模板名
$templateid 表示模板id
$tpldir 表示模板目录
global $tplrefresh;这个是把$tplrefresh作为一个全局变量,tplrefresh表示是不是刷新模板
CODE:
$tpldir = $tpldir ? $tpldir : TPLDIR;
$templateid = $templateid ? $templateid : TEMPLATEID;给$tpldir和$templateid赋值,如果没有传进来就用常量TPLDIR和TEMPLATEID给它们值
$tplfile = DISCUZ_ROOT.’./’.$tpldir.’/’.$file.’.htm’;
$objfile = DISCUZ_ROOT.’./forumdata/templates/’.$templateid.’_’.$file.’.tpl.php’;这里是把$tplfile(表示的是模板文件)和$objfile(表示的是要编译成的文件)赋值
if(TEMPLATEID != 1 && $templateid != 1 && !file_exists($tplfile)) {
return template($file, 1, ‘./templates/default/’);
}
防止TEMPLATEID不等于1且$templateid不等于1,而且模板文件不存在导致空白问题。
这里也可以算一个迭代,也可以不算,就是把1作为第二个参数再调用函数本身。
if($tplrefresh == 1 || ($tplrefresh > 1 && substr($GLOBALS['timestamp'], -1) > $tplrefresh)) {
if(@filemtime($tplfile) > @filemtime($objfile)) {
require_once DISCUZ_ROOT.’./include/template.func.php’;
parse_template($file, $templateid, $tpldir);
}
}
return $objfile;
很巧妙的一段,Discuz的模板缓存就体现在这里,如果你没打开模板刷新的话(config.inc.php->$tplrefresh=0),这里就直接返回一个$objfile了,而这个文件是你第一次建论坛的时候就写入的,如果你不改模板的话,关了是能提高相当一部分效率的!反之,如果你打开了模板刷新的话就接着判断是不是模板文件的建立时间大于 forumdata/templates下的文件,是的话就引用./include/template.func.php写入模板文件到 forumdata/templates中,否则的话还是直接返回一个已经编译好的模板文件。
关于template.func.php文件中函数的分析在下面:
005 | if (!defined( 'IN_DISCUZ' )) {<br> |
007 | exit ( 'Access Denied' );<br> |
011 | function parse_template( $file , $templateid , $tpldir ) {<br> |
013 | global $language ;<br> |
017 | $tplfile = DISCUZ_ROOT. "./$tpldir/$file.htm" ;<br> |
019 | $objfile = DISCUZ_ROOT. "./forumdata/templates/{$templateid}_$file.tpl.php" ;<br> |
021 | if (!@ $fp = fopen ( $tplfile , 'r' )) {<br> |
023 | dexit( "Current template file './$tpldir/$file.htm' not found or have no access!" );<br> |
025 | } elseif (! include_once language( 'templates' , $templateid , $tpldir )) {<br> |
027 | dexit( "<br>Current template pack do not have a necessary language file 'templates.lang.php' or have syntax error!" );<br> |
031 | $template = fread ( $fp , filesize ( $tplfile ));<br> |
035 | $var_regexp = "((\\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)(\[[a-zA-Z0-9_\-\.\"\'\[\]\$\x7f-\xff]+\])*)" ;<br> |
037 | $const_regexp = "([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)" ;<br> |
039 | $template = preg_replace( "/([\n\r]+)\t+/s" , "\\1" , $template );<br> |
041 | $template = preg_replace( "/\<\!\-\-\{(.+?)\}\-\-\>/s" , "{\\1}" , $template );<br> |
043 | $template = preg_replace( "/\{lang\s+(.+?)\}/ies" , "languagevar('\\1')" , $template );<br> |
045 | $template = preg_replace( "/\{faq\s+(.+?)\}/ies" , "faqvar('\\1')" , $template );<br> |
047 | $template = str_replace ( "{LF}" , "<?=\"\\n\"?>" , $template );<br> |
049 | $template = preg_replace( "/\{(\\\$[a-zA-Z0-9_\[\]\'\"\$\.\x7f-\xff]+)\}/s" , "<?=\\1?>" , $template );<br> |
051 | $template = preg_replace( "/$var_regexp/es" , "addquote('<?=\\1?>')" , $template );<br> |
053 | $template = preg_replace( "/\<\?\=\<\?\=$var_regexp\?\>\?\>/es" , "addquote('<?=\\1?>')" , $template );<br> |
055 | $template = "<? if(!defined('IN_DISCUZ')) exit('Access Denied'); ?>\n$template" ;<br> |
057 | $template = preg_replace( "/[\n\r\t]*\{template\s+([a-z0-9_]+)\}[\n\r\t]*/is" , "\n<? include template('\\1'); ?>\n" , $template );<br> |
059 | $template = preg_replace( "/[\n\r\t]*\{template\s+(.+?)\}[\n\r\t]*/is" , "\n<? include template(\\1); ?>\n" , $template );<br> |
061 | $template = preg_replace( "/[\n\r\t]*\{eval\s+(.+?)\}[\n\r\t]*/ies" , "stripvtags('\n<? \\1 ?>\n','')" , $template );<br> |
063 | $template = preg_replace( "/[\n\r\t]*\{echo\s+(.+?)\}[\n\r\t]*/ies" , "stripvtags('\n<? echo \\1; ?>\n','')" , $template );<br> |
065 | $template = preg_replace( "/[\n\r\t]*\{elseif\s+(.+?)\}[\n\r\t]*/ies" , "stripvtags('\n<? } elseif(\\1) { ?>\n','')" , $template );<br> |
067 | $template = preg_replace( "/[\n\r\t]*\{else\}[\n\r\t]*/is" , "\n<? } else { ?>\n" , $template );<br> |
069 | for ( $i = 0; $i < $nest ; $i ++) {<br> |
071 | $template = preg_replace( "/[\n\r\t]*\{loop\s+(\S+)\s+(\S+)\}[\n\r]*(.+?)[\n\r]*\{\/loop\}[\n\r\t]*/ies" , "stripvtags('\n<? if(is_array(\\1)) { foreach(\\1 as \\2) { ?>','\n\\3\n<? } } ?>\n')" , $template );<br> |
073 | $template = preg_replace( "/[\n\r\t]*\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}[\n\r\t]*(.+?)[\n\r\t]*\{\/loop\}[\n\r\t]*/ies" , "stripvtags('\n<? if(is_array(\\1)) { foreach(\\1 as \\2 => \\3) { ?>','\n\\4\n<? } } ?>\n')" , $template );<br> |
075 | $template = preg_replace( "/[\n\r\t]*\{if\s+(.+?)\}[\n\r]*(.+?)[\n\r]*\{\/if\}[\n\r\t]*/ies" , "stripvtags('\n<? if(\\1) { ?>','\n\\2\n<? } ?>\n')" , $template );<br> |
078 | $template = preg_replace( "/\{$const_regexp\}/s" , "<?=\\1?>" , $template );<br> |
080 | $template = preg_replace( "/ \?\>[\n\r]*\<\? /s" , " " , $template );<br> |
084 | if (!@ $fp = fopen ( $objfile , 'w' )) {<br> |
086 | dexit( "Directory './forumdata/templates/' not found or have no access!" );<br> |
092 | $template = preg_replace( "/\"(http)?[\w\.\/:]+\?[^\"]+?&[^\"]+?\"/e" , "transamp('\\0')" , $template );<br> |
094 | $template = preg_replace( "/\<script[^\>]*?src=\"(.+?)\".*?\>\s*\<\/script\>/ise" , "stripscriptamp('\\1')" , $template );<br> |
098 | fwrite( $fp , $template );<br> |
104 | function transamp( $str ) {<br> |
106 | $str = str_replace ( '&' , '&' , $str );<br> |
108 | $str = str_replace ( '&' , '&' , $str );<br> |
110 | $str = str_replace ( '\"' , '"' , $str );<br> |
116 | function addquote( $var ) {<br> |
118 | return str_replace ( "\\\"" , "\"" , preg_replace( "/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\]/s" , "['\\1']" , $var ));<br> |
122 | function languagevar( $var ) {<br> |
124 | if (isset( $GLOBALS [ 'language' ][ $var ])) {<br> |
126 | return $GLOBALS [ 'language' ][ $var ];<br> |
136 | function faqvar( $var ) {<br> |
140 | include_once DISCUZ_ROOT. './forumdata/cache/cache_faqs.php' ;<br> |
144 | if (isset( $_DCACHE [ 'faqs' ][ $var ])) {<br> |
146 | return '<a href="faq.php?action=message&id=' . $_DCACHE [ 'faqs' ][ $var ][ 'id' ]. '" target="_blank">' . $_DCACHE [ 'faqs' ][ $var ][ 'keyword' ]. '</a>' ;<br> |
155 | function stripvtags( $expr , $statement ) {<br> |
157 | $expr = str_replace ( "\\\"" , "\"" , preg_replace( "/\<\?\=(\\\$.+?)\?\>/s" , "\\1" , $expr ));<br> |
159 | $statement = str_replace ( "\\\"" , "\"" , $statement );<br> |
161 | return $expr . $statement ;<br> |
163 | } </p><p> function stripscriptamp( $s ) {<br> |
165 | $s = str_replace ( '&' , '&' , $s );<br> |
167 | return "<script src=\"$s\" type=\"text/javascript\"></script>" ;<br> |