Discuz模板解析原理template.func.php

最近正在领略discuz的强大,尤其是对模板解析这一块,网上搜了一下貌似早有牛人做了分析,虽然是07年的东西不过现在拿来看看还是获益匪浅的。。废话不说看原文吧! 

./include/template.func.php 

本帖不仅仅是分析文件,还把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 

代码: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; 
} 

这个函数一共有三个传入参数: 
$file 表示模板名 
$templateid 表示模板id 
$tpldir 表示模板目录引用: 

global $tplrefresh; 

这个是把$tplrefresh作为一个全局变量,tplrefresh表示是不是刷新模板引用: 

$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文件中函数的分析在下面: 

代码://防止非法引用 
if(!defined('IN_DISCUZ')) { 
exit('Access Denied'); 
}代码:/** 
* 这个是关键模板解析函数,通过正则表达式来替换掉模板中的相关语句,使之变成标准的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); 
}代码:/** 
* 几个替换,过滤掉一些东西 
* @para string $str 
* 
* @return string 
*/ 

function transamp($str) { 
$str = str_replace('&', '&', $str); 
$str = str_replace('&', '&', $str); 
$str = str_replace('\\"', '\"', $str); 
return $str; 
}代码:/** 
* 从正则表达式来看是给ubb代码去掉一个\符号的,应该是为安全性着想的 
* @para string $val 
* 
* @return string 
*/ 

function addquote($var) { 
return str_replace(\"\\\\"\", \"\\"\", preg_replace(\"/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\]/s\", \"['\\1']\", $var)); 
}代码:/** 
* 把一个字符用语言包中的来替换,没有找到的话就用!string来替换 
* @para string $val 
* 
* @return string 
*/ 

function languagevar($var) { 
if(isset($GLOBALS['language'][$var])) { 
return $GLOBALS['language'][$var]; 
} else { 
return \"!$var!\"; 
} 
}代码:/** 
* 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!\"; 
} 
}代码:/** 
* 去掉自定义的一些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; 
}代码:/** 
* 作用是把&符号的html编码形式换成&,然后换成javascript引用的形式。 
* @para string $s 
* 
* @return string 
*/ 

function stripscriptamp($s) { 
$s = str_replace('&', '&', $s); 
return \"<script src=\\"$s\\" type=\\"text/javascript\\"></script>\"; 
}

你可能感兴趣的:(template)