剛開始學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> |