Discuz!源代碼分析系列(4)-./include/template.func.php(模板)

本帖不單單是分析文件,還把Discuz中模板解析這一原理分析了一下。轉自 www.discuz.net 做者:郭鑫

我記得我剛開始學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
CODE:
function template($file, $templateid = 0, $tpldir = '') {
global $tplrefresh;

$tpldir = $tpldir ? $tpldir : TPLDIR;
$templateid = $templateid ? $templateid : TEMPLATEID;

$tplfile = DISCUZ_ROOT.'./'.$tpldir.'/'.$file.'.htm';
$objfile = DISCUZ_ROOT.'./forumdata/templates/'.$templateid.'_'.$file.'.tpl.php';
if(TEMPLATEID != 1 && $templateid != 1 && !file_exists($tplfile)) {
return template($file, 1, './templates/default/');
}
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;
}
這個函數一共有三個傳入參數:
QUOTE:
$file 表示模板名
$templateid 表示模板id
$tpldir 表示模板目錄
CODE:
global $tplrefresh;這個是把$tplrefresh做爲一個全局變量,tplrefresh表示是否是刷新模板
CODE:
$tpldir = $tpldir ? $tpldir : TPLDIR;
$templateid = $templateid ? $templateid : TEMPLATEID;
給$tpldir和$templateid賦值,若是沒有傳進來就用常量TPLDIR和TEMPLATEID給它們值
CODE:
$tplfile = DISCUZ_ROOT.'./'.$tpldir.'/'.$file.'.htm';
$objfile = DISCUZ_ROOT.'./forumdata/templates/'.$templateid.'_'.$file.'.tpl.php';
這裏是把$tplfile(表示的是模板文件)和$objfile(表示的是要編譯成的文件)賦值
CODE:
if(TEMPLATEID != 1 && $templateid != 1 && !file_exists($tplfile)) {
return template($file, 1, './templates/default/');
}
防止TEMPLATEID不等於1且$templateid不等於1,並且模板文件不存在致使空白問題。
這裏也能夠算一個迭代,也能夠不算,就是把1做爲第二個參數再調用函數自己。
技術末學 發佈於2007-07-04 10:18:09
CODE:
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文件中函數的分析在下面:
CODE:
//防止非法引用
if(!defined('IN_DISCUZ')) {
exit('Access Denied');
}
CODE:
/**
* 這個是關鍵模板解析函數,經過正則表達式來替換掉模板中的相關語句,使之變成標準的php語句,寫入緩存(./forumdata/template),從而include到頁面中顯示出來
* @para string $file //解析的模板名,只要文件名就能夠了,會自動加上htm,若是有緩存的話就變成"模板id_文件名.tpl.php"
* @para int $templateid //模板的id
* @para string $tpldir //模板所在的目錄
*
*/

function parse_template($file, $templateid, $tpldir) {
global $language;

$nest = 5;
$tplfile = DISCUZ_ROOT."./$tpldir/$file.htm";
$objfile = DISCUZ_ROOT."./forumdata/templates/{$templateid}_$file.tpl.php";

if(!@$fp = fopen($tplfile, 'r')) {
dexit("Current template file './$tpldir/$file.htm' not found or have no access!");
} elseif(!include_once language('templates', $templateid, $tpldir)) {
dexit("<br>Current template pack do not have a necessary language file 'templates.lang.php' or have syntax error!");
}

$template = fread($fp, filesize($tplfile));
fclose($fp);

$var_regexp = "((\\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)(\[[a-zA-Z0-9_\-\.\"\'\[\]\$\x7f-\xff]+\])*)";
$const_regexp = "([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)";

$template = preg_replace("/([\n\r]+)\t+/s", "\\1", $template);
$template = preg_replace("/\<\!\-\-\{(.+?)\}\-\-\>/s", "{\\1}", $template);
$template = preg_replace("/\{lang\s+(.+?)\}/ies", "languagevar('\\1')", $template);
$template = preg_replace("/\{faq\s+(.+?)\}/ies", "faqvar('\\1')", $template);
$template = str_replace("{LF}", "<?=\"\\n\"?>", $template);

$template = preg_replace("/\{(\\\$[a-zA-Z0-9_\[\]\'\"\$\.\x7f-\xff]+)\}/s", "<?=\\1?>", $template);
$template = preg_replace("/$var_regexp/es", "addquote('<?=\\1?>')", $template);
$template = preg_replace("/\<\?\=\<\?\=$var_regexp\?\>\?\>/es", "addquote('<?=\\1?>')", $template);

$template = "<? if(!defined('IN_DISCUZ')) exit('Access Denied'); ?>\n$template";
$template = preg_replace("/[\n\r\t]*\{template\s+([a-z0-9_]+)\}[\n\r\t]*/is", "\n<? include template('\\1'); ?>\n", $template);
$template = preg_replace("/[\n\r\t]*\{template\s+(.+?)\}[\n\r\t]*/is", "\n<? include template(\\1); ?>\n", $template);
$template = preg_replace("/[\n\r\t]*\{eval\s+(.+?)\}[\n\r\t]*/ies", "stripvtags('\n<? \\1 ?>\n','')", $template);
$template = preg_replace("/[\n\r\t]*\{echo\s+(.+?)\}[\n\r\t]*/ies", "stripvtags('\n<? echo \\1; ?>\n','')", $template);
$template = preg_replace("/[\n\r\t]*\{elseif\s+(.+?)\}[\n\r\t]*/ies", "stripvtags('\n<? } elseif(\\1) { ?>\n','')", $template);
$template = preg_replace("/[\n\r\t]*\{else\}[\n\r\t]*/is", "\n<? } else { ?>\n", $template);

for($i = 0; $i < $nest; $i++) {
$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);
$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);
$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);
}

$template = preg_replace("/\{$const_regexp\}/s", "<?=\\1?>", $template);
$template = preg_replace("/ \?\>[\n\r]*\<\? /s", " ", $template);

if(!@$fp = fopen($objfile, 'w')) {
dexit("Directory './forumdata/templates/' not found or have no access!");
}

$template = preg_replace("/\"(http)?[\w\.\/:]+\?[^\"]+?&[^\"]+?\"/e", "transamp('\\0')", $template);
$template = preg_replace("/\<script[^\>]*?src=\"(.+?)\".*?\>\s*\<\/script\>/ise", "stripscriptamp('\\1')", $template);

flock($fp, 2);
fwrite($fp, $template);
fclose($fp);
}
CODE:
/**
* 幾個替換,過濾掉一些東西
* @para string $str
*
* @return string
*/

function transamp($str) {
$str = str_replace('&', '&', $str);
$str = str_replace('&', '&', $str);
$str = str_replace('\"', '"', $str);
return $str;
}
CODE:
/**
* 從正則表達式來看是給ubb代碼去掉一個\符號的,應該是爲安全性着想的
* @para string $val
*
* @return string
*/

function addquote($var) {
return str_replace("\\\"", "\"", preg_replace("/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\]/s", "['\\1']", $var));
}

技術末學的我的空間 技術末學 發佈於2007-07-04 10:22:56
CODE:
/**
* 把一個字符用語言包中的來替換,沒有找到的話就用!string來替換
* @para string $val
*
* @return string
*/

function languagevar($var) {
if(isset($GLOBALS['language'][$var])) {
return $GLOBALS['language'][$var];
} else {
return "!$var!";
}
}
CODE:
/**
* FAQ中把id和關建字用一個有效的連接來替換
* @para array $val
*
* @return string
*/

function faqvar($var) {
global $_DCACHE;
include_once DISCUZ_ROOT.'./forumdata/cache/cache_faqs.php';

if(isset($_DCACHE['faqs'][$var])) {
return '<a href="faq.php?action=message&id='.$_DCACHE['faqs'][$var]['id'].'" target="_blank">'.$_DCACHE['faqs'][$var]['keyword'].'</a>';
} else {
return "!$var!";
}
}
CODE:
/**
* 去掉自定義的一些tag
* @para string $expr
* @para string $statement
* @return string
*/

function stripvtags($expr, $statement) {
$expr = str_replace("\\\"", "\"", preg_replace("/\<\?\=(\\\$.+?)\?\>/s", "\\1", $expr));
$statement = str_replace("\\\"", "\"", $statement);
return $expr.$statement;
}
CODE:
/** * 做用是把&符號的html編碼形式換成&,而後換成javascript引用的形式。 * @para string $s * * @return string */ function stripscriptamp($s) { $s = str_replace('&', '&', $s); return "<script src=\"$s\" type=\"text/javascript\"></script>"; }
相關文章
相關標籤/搜索