discuz模板机制

[复制链接]
ordersy 发表于 2019-2-6 10:47:55 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 ordersy 于 2019-2-6 10:49 编辑

discuz采用‘编译型’模版机制,其核心思想是将页面布局以普通网页文件存贮,在这些文件中插入需要动态显示数据的“仿PHP代码”,模版系统再把模版源文件中的“仿PHP代码”通过DZ的模版的引擎转换为真正的PHP代码并将其另存为PHP程序格式的“模版缓存文件”,这个转换过程常常被称为“编译”。
在用户浏览的时候,模版系统载入“模版缓存文件”,将PHP程序运行的结果带入这些文件后执行输出,当模版源文件丢失或其内容被修改后,模版系统会自动检测并重建“模版缓存文件”。
template()函数的作用是将参数$tFile表示的模版;源文件‘编译’为目标‘模版缓存文件’,参数$templateid表示模版系统编号,用以区分相同名称不同套系的模版缓存文件。模版“编译”的原理是使用文件读取函数读入模版文件,然后使用正则表达式模版文件中的‘仿PHP代码’替换成真正的PHP代码,写入到指定目录下的目标‘模版缓存文件’。
在Disvuz中当仿PHP代码中if分支和loop循环结构最多为6层。
Section One–./include/global.func.php—->template function

function template($file, $templateid = 0, $tpldir = '') {     [color=rgb(0, 0, 136) !important]global $tplrefresh;       $tpldir = $tpldir ? $tpldir : TPLDIR;     $templateid = $templateid ? $templateid : TEMPLATEID;     $tplfile = DISCUZ_ROOT.[color=rgb(0, 153, 0) !important]'./'.$tpldir.[color=rgb(0, 153, 0) !important]'/'.$file.[color=rgb(0, 153, 0) !important]'.htm';     $objfile = DISCUZ_ROOT.[color=rgb(0, 153, 0) !important]'./forumdata/templates/'.$templateid.[color=rgb(0, 153, 0) !important]'_'.$file.[color=rgb(0, 153, 0) !important]'.tpl.php';     [color=rgb(0, 0, 136) !important]if(TEMPLATEID != [color=rgb(0, 102, 102) !important]1 && $templateid != [color=rgb(0, 102, 102) !important]1 && !file_exists($tplfile)) {           [color=rgb(0, 0, 136) !important]return template($file, [color=rgb(0, 102, 102) !important]1, [color=rgb(0, 153, 0) !important]'./templates/default/');        }      [color=rgb(0, 0, 136) !important]if($tplrefresh == [color=rgb(0, 102, 102) !important]1 || ($tplrefresh > [color=rgb(0, 102, 102) !important]1 && substr($GLOBALS[[color=rgb(0, 153, 0) !important]'timestamp'], -[color=rgb(0, 102, 102) !important]1) > $tplrefresh)) {         [color=rgb(0, 0, 136) !important]if(@filemtime($tplfile) > @filemtime($objfile)) {             [color=rgb(0, 0, 136) !important]require_once DISCUZ_ROOT.[color=rgb(0, 153, 0) !important]'./include/template.func.php';              parse_template($file, $templateid, $tpldir);        }    }             [color=rgb(0, 0, 136) !important]return $objfile; }
这个函数一共有三个传入参数:     $file 表示模板名    $templateid 表示模板id    $tpldir 表示模板目录    [color=rgb(0, 0, 136) !important]global $tplrefresh;这个是把$tplrefresh作为一个全局变量,tplrefresh表示是不是刷新模板     $tpldir = $tpldir ? $tpldir : TPLDIR;    $templateid = $templateid ? $templateid : TEMPLATEID;给$tpldir$templateid赋值,如果没有传进来就用常量TPLDIR和TEMPLATEID给它们值     $tplfile = DISCUZ_ROOT.[color=rgb(0, 153, 0) !important]'./'.$tpldir.[color=rgb(0, 153, 0) !important]'/'.$file.[color=rgb(0, 153, 0) !important]'.htm';     $objfile = DISCUZ_ROOT.[color=rgb(0, 153, 0) !important]'./forumdata/templates/'.$templateid.[color=rgb(0, 153, 0) !important]'_'.$file.[color=rgb(0, 153, 0) !important]'.tpl.php'; 这里是把$tplfile(表示的是模板文件)和$objfile(表示的是要编译成的文件)赋值     [color=rgb(0, 0, 136) !important]if(TEMPLATEID != [color=rgb(0, 102, 102) !important]1 && $templateid != [color=rgb(0, 102, 102) !important]1 && !file_exists($tplfile)) {       [color=rgb(0, 0, 136) !important]return template($file, [color=rgb(0, 102, 102) !important]1, ‘./templates/[color=rgb(0, 0, 136) !important]default/’);} 防止TEMPLATEID不等于[color=rgb(0, 102, 102) !important]1$templateid不等于[color=rgb(0, 102, 102) !important]1,而且模板文件不存在导致空白问题。这里也可以算一个迭代,也可以不算,就是把[color=rgb(0, 102, 102) !important]1作为第二个参数再调用函数本身。[color=rgb(0, 0, 136) !important]if($tplrefresh == [color=rgb(0, 102, 102) !important]1 || ($tplrefresh > [color=rgb(0, 102, 102) !important]1 && substr($GLOBALS[‘timestamp’], -[color=rgb(0, 102, 102) !important]1) > $tplrefresh)) {    [color=rgb(0, 0, 136) !important]if(@filemtime($tplfile) > @filemtime($objfile)) {       [color=rgb(0, 0, 136) !important]require_once DISCUZ_ROOT.[color=rgb(0, 153, 0) !important]'./include/template.func.php';              parse_template($file, $templateid, $tpldir);        }}        [color=rgb(0, 0, 136) !important]return $objfile;很巧妙的一段,Discuz的模板缓存就体现在这里,如果你没打开模板刷新的话(config.inc.php->$tplrefresh=[color=rgb(0, 102, 102) !important]0),这里就直接返回一个$objfile了,而这个文件是你第一次建论坛的时候就写入的,如果你不改模板的话,关了是能提高相当一部分效率的!反之,如果你打开了模板刷新的话就接着判断是不是模板文件的建立时间大于 forumdata/templates下的文件,是的话就引用./[color=rgb(0, 0, 136) !important]include/template.func.php写入模板文件到 forumdata/templates中,否则的话还是直接返回一个已经编译好的模板文件。

我们来了解一下PHP的工作,
PHP是一种可以混合php代码和HTML代码的语言
比如在一个PHP文件里,可以直接写入HTML代码,然后在需要写php代码的地方加上<?php ?>,在里面写代码就可以了,这种模板和代码混合的写法称为原生态写法,现在流行的wordpress就是这种写法,很多高手崇尚这种写法。比如这个代码就是原生态的写法完成的:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">    [color=rgb(0, 102, 102) !important]<html xmlns="http://www.w3.org/1999/xhtml" <?php language_attributes(); ?>>    [color=rgb(0, 102, 102) !important]<head>    [color=rgb(0, 102, 102) !important]<title><?php bloginfo('name'); ?><?php echo $title; ?>[color=rgb(0, 102, 102) !important]</title>    [color=rgb(0, 102, 102) !important]<meta http-equiv="Content-Type" content="<?php bloginfo('html_type'); ?>; charset=<?php bloginfo('charset'); ?>" />    <?php    wp_admin_css( 'login', true );    wp_admin_css( 'colors-fresh', true );    if ( $is_iphone ) { ?>

但是我个人看了这种代码是会头疼的。。。因为太乱了。。。又不美观。。。而且如果很多人来写这个工程,就乱套了。能不能把模板和代码分开呢?这样美工就能专注模板了。。。
所以DISCUZ也这么干了。
我们把所有的PHP代码单独写在一起,放在某个页面的上面,里面都是一些赋值了的变量,比如这个帖子的内容。
下面再载入模板,就像我们上面看到的分析一样,那个第三步就相当于把那个模板页面所有内容放到这个forumdisplay页面的最下面
当然,这个包含进来的模板是经过编译的,也就是把那个特有我们写的模板转变成PHP和HTML混合的代码。
所以最后我们得到的这个PHP页面里究竟变成了什么了呢?
有童鞋说出来了,这样得到的不就是一个混合了HTML和PHP代码的原生态写法的php文件吗?对的。。。模板编译的结果就是最后给服务器运行一个原生态写法的php文件。
由于discuz模板编译的过程,是把templates文件下里对应的模板文件转变成原生态的写法,然后存放在cache的文件夹里,然后再包含进根目录下的文件里,所以大家也可以在缓存文件夹里的模板文件夹里查看到某个页面经过编译的原生态的php代码。

上面说了,经过模板编译的过程,把templates里面的htm文件变成原生态写法的php文件,那么我们可以肯定,上面的template函数肯定经过了一些特殊处理,把一定格式的htm文件转变成php文件,而这个htm文件一定要遵循某些符合这个函数转变规律的写法才行,否则就会编译错误。
而这个写法也就是我们这堂课的重点了。

这个函数的作用是检测已经存在模板缓存文件是否过期或者被强制更新,如果需要重新编译,则导入专门处理模板的文件/include/template.func.php,然后使用parse_template函数,对这个模板进行处理。
我们来看看parse_template函数,其实看名字也知道,这个才是真正的重中之重,分析这个里面的规律就可以知道其编译的规律了。

    function tplParse($tFile,$cFile){        $fileContent=file_get_contents($tFile);        $fileContent=preg_replace_callback([color=rgb(0, 153, 0) !important]"/^(\xef\xbb\xbf)/", function($r){}, $fileContent);        $fileContent=preg_replace_callback(            [color=rgb(0, 153, 0) !important]"/\<\!\-\-\s*\\\$\{(.+?)\}\s*\-\-\>/is",            function($r)            {                [color=rgb(0, 0, 136) !important]return str_replace([color=rgb(0, 153, 0) !important]'\"', [color=rgb(0, 153, 0) !important]'"', [color=rgb(0, 153, 0) !important]'<?php echo ' . $r[[color=rgb(0, 102, 102) !important]1] . [color=rgb(0, 153, 0) !important]'; ?>');            },            $fileContent        );        $fileContent=preg_replace_callback(            [color=rgb(0, 153, 0) !important]"/\{(\\\$[a-zA-Z0-9_\[\]\\\ \-\'\,\%\*\/\.\(\)\>\'\"\$\x7f-\xff]+)\}/s",            function($r)            {                [color=rgb(0, 0, 136) !important]return str_replace([color=rgb(0, 153, 0) !important]'\"', [color=rgb(0, 153, 0) !important]'"', [color=rgb(0, 153, 0) !important]'<?php echo ' . $r[[color=rgb(0, 102, 102) !important]1] . [color=rgb(0, 153, 0) !important]'; ?>');            },            $fileContent        );        $fileContent = preg_replace_callback(            [color=rgb(0, 153, 0) !important]"#<!--\s*{\s*include\s+([^\{\}]+)\s*\}\s*-->#i",             function($r)            {                [color=rgb(0, 0, 136) !important]return [color=rgb(0, 153, 0) !important]'<?php include template("'.$r[[color=rgb(0, 102, 102) !important]1].[color=rgb(0, 153, 0) !important]'");?>';            },            $fileContent        );.........        file_put_contents($cFile,$fileContent);        [color=rgb(0, 0, 136) !important]return [color=rgb(0, 0, 136) !important]true;    }

这个里面是不是很多正则式匹配的过程啊,还有很多的preg_replace,当然。。。这里就是把htm里面的所有代码进行一次一次按规律把HTML和专用语法代码转变成标准的HTML+PHP代码的过程了。
替换次数很多,所以上面的代码省略了。
比如我们在模板里写入这句:

[color=rgb(0, 102, 102) !important]<h2>{$array[a]}[color=rgb(0, 102, 102) !important]</h2>

经过处理,就会变成这样的原生态写法:

<h2>[color=rgb(0, 153, 0) !important]<?php [color=rgb(0, 0, 136) !important]echo $array[[color=rgb(0, 153, 0) !important]'a']; [color=rgb(0, 153, 0) !important]?></h2>

上面的在php服务器上不能运行,而下面的经过编译了,就可以直接运行了。
在PHP模板里我们还能写入PHP语句的,当然这个也要经过编译才能变成直接运行的php代码,比如if,else等,还有循环loop,用来变成foreach。这些在模板里作用很多,比如一个tab页面,大多是通过地址传值,然后经过IF等处理后,展示出来。这些固定的写法是必须背诵的,下面我会详细介绍,其实也是这个文里面最重要的部分了。

看了上面的部分,同学们已经了解了整个模板在DISCUZ中的处理过程了。那么我们来制作模板,知道这个原理后,最重要的可能就是要了解他们的语法了。
然后才好自己随心所欲的编写模板。
我总结了一下,下面会分条列出:
1,首先要说的是变量,我们从上面的分析可以知道,在根目录文件里,首先已经把变量都赋值好了。在模板里直接应用就可以了,而这个变量在DISCUZ模板里要这样写:

{$a},{$a},{AAA}

这里分别代表了变量,数组中某个值,常量的写法。他们相当于:<?php echo $a ?>等语法。
2,模板语法。在模板里也能运用语法,这样可以判断然后展示出想要的模板样式。
首先说一下他们的通用写法,都要写在这个里面:

<!--{}-->

大家看这个是不是很熟悉啊,当然,细心的同学,我们在上一节课里已经说了注释的标示是<!---->
在这里多了一个{}然后里面就能写判断等语句了,写法如下:

<!--{if}--><!--{/if}--><!--{if}--><!--{else}--><!--{/if}--><!--{if}--><!--{elseif }--><!--{else}--><!--{if}-->

比如:

<!--{if $userid}-->欢迎您<!--{else}-->请登陆<!--{/if}-->

这就是一个简单的模板判断语句,判断用户时候有登陆
3,循环语句。这个东西太重要了。大家务必掌握。

<!--{loop $array $key $value)}--> <!--{/loop}-->

这个句法相当于

foreach($array as $key=>$value) {}

作用就是把数组遍历输出。
里面的$array就是数据,可以是一维数组或者多维,多维的情况下可以使用$key[][]等方式展示出来。
这里的$key不一定要写出来,不写出来的时候,相当于直接遍历值而不用考虑键名。
比如首页的用户列表,帖子列表神马的都是把信息放入数组,然后在模板里遍历输出。

同学们看到这里可能有疑问,为啥米他们要用foreach而不用for或者while呢?这个问题你们怎么不去问DZ开发人员?
不过如果让我来写,我也会用foreach的 ,原因不明。。。
4,{lang }
载入语言包,他们很复杂的把所有的中文写到了语言包里去了。这样有一定的好处,不过吾觉得实在是太麻烦了。

5,{template }
载入另外一个模板。

6,{eval }
这个后面可以直接运行php代码,比如{eval echo ‘hello!’;},这个用多了会显得不专业,所以不要轻易用!

7,{LF}
换行符,大家从上面的代码也可以看出来

$template = str_replace("{LF}", "<?="\n"?>", $template);

8,{subtemplate }
这个也是载入,不过不会对载入的进行处理,可以理解为直接引用。一般在做边栏模块的时候用的多。
9,{csstemplate }
专门载入CSS文件的,因为在CSS文件中,我们会发现那个CSS也是htm结尾的,而且里面到底是使用哪个文件夹里的背景图片等等也是需要经过编译的。

10,{echo }
这个不用介绍了吧。不过这个你也能使用到的话,只能说明你要么是白痴要么是天才。

大熊猫 发表于 2019-2-17 17:30:50 | 显示全部楼层
楼主注意一下阅读体验,格式太乱了
*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则