不积跬步无以至千里

记录精彩的程序人生

  • 首页
  • Java
  • Golang
  • PHP
  • Python
  • Nodejs
  • Lua
  • Docker
  • DevOps
  • 文章归档
  • 默认分类
  • 关于
  • 标签

  • 搜索
PostgreSQL hbase 时间同步 nexus 开机启动 nexus, 开机启动 jenkins安装配置 gitlab安装配置 gitlab安装 文件系统 fastdfs gcc切换 gcc升级 mysql8 交换空间 虚拟内存 tcp thrift lua tag test VPN SoftEtherVPN homebrew asm spring tomcat maven jdk ios mac 图案字符 figlet mysql半同步复制 mysql主从同步 一主多从 一主一从 主从同步 反向代理 密码重置 test 虚拟机扩容 swap 虚拟空间 docker ldocker grpc-gateway protobuf 微服务 go grp GRPC 授权登录 OAuth2.0 SOA supervisord supervisor RPC CentOS rabbitmq 环境变量 php-fpm php.ini error php7 lnmp 编译安装 mysql nginx linux java php python redis 字符串操作 mysql5.7 Solo

DISCUZ模板编译原理

发表于 2020-03-21 | 分类于 默认分类 | 0 | 阅读次数 2898

先以7.X系列为例子,我们打开一个论坛,在域名后面会有一个文件,这个文件可能是index.php,forumdisplay.php等等。
大家解压discuz的压缩包看看,在根目录下,也有这么一些文件,大家去看看,是不是我们能够直接输入的地址的文件在这里基本都有啦?

我们打开帖子列表,forumdisplay.php?fid=1为例子,就会看到一个包含页头页尾精美样式和信息的HTML页面。
但是我们打开这个php文件,却发现里面的东西并不多。
这是什么缘故呢?

我们现在首先来研究一下打开一个页面的原理。 1,包含进了一些重要的文件。
  1. require_once './include/common.inc.php';
  2. require_once DISCUZ_ROOT.'./include/forum.func.php';
复制代码
第一个文件里,服务器会判断用户类型,保持用户状态,判断论坛开启状态,初始化数据库,载入常用函数库等。 在第二个文件里,会包含进与论坛相关的函数库。 2,接下来看到的将会是大段的代码,我们仔细查看会发现这些无非是一些判断,然后是赋值。 这里出现了很多的变量。所以这个过程可以看作是获取这个页面的所有的信息的过程,比如帖子列表,用户名列表等数组信息。 3,看最后一句,有这样的代码:
  1. include template($template);
复制代码
大家可以从英文单词里判断,这个一定是和模板有关吧? 等到最后,原来所看到的HTML到最后才载入啊。。。
所以从上面的分析我们可以基本知道这个根目录下的文件的工作过程。 而我们知道,如果是一个纯静态空间,里面都是HTML代码,我们也可以做出和论坛一模一样的网站。 但是却不能自动更新。 所以我们也可以分析出,我们的PHP空间最后输出的也肯定是和上面的一样的HTML代码。 不同的是静态空间的文件是事先生成好的,而我们通过DZ这个系统,通过PHP空间工作,最后也生成和上面一样的HTML代码。

说到这里,同学们肯定会问:那么discuz系统的HTML是在哪里生成的呢?又要如何改变他呢?
休息一下,本公子大便一下先……速速就回……

好吧,大便回来神清气爽嘿,还真对得起咱这张脸

我们来了解一下PHP的工作, PHP是一种可以混合php代码和HTML代码的语言 比如在一个PHP文件里,可以直接写入HTML代码 然后在需要写php代码的地方加上<?php ?>,在里面写代码就可以了 这种模板和代码混合的写法称为原生态写法,现在流行的wordpress就是这种写法,很多高手崇尚这种写法。 比如这个代码就是原生态的写法完成的:
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml" <?php language_attributes(); ?>>
  3. <head>
  4. <title><?php bloginfo('name'); ?> &rsaquo; <?php echo $title; ?></title>
  5. <meta http-equiv="Content-Type" content="<?php bloginfo('html_type'); ?>; charset=<?php bloginfo('charset'); ?>" />
  6. <?php
  7. wp_admin_css( 'login', true );
  8. wp_admin_css( 'colors-fresh', true );
  9. if ( $is_iphone ) { ?>
复制代码
但是我个人看了这种代码是会头疼的。。。因为太乱了。。。又不美观。。。而且如果很多人来写这个工程,就乱套了。 能不能把模板和代码分开呢?这样美工就能专注模板了。。。 所以DISCUZ也这么干了。 我们把所有的PHP代码单独写在一起,放在某个页面的上面,里面都是一些赋值了的变量,比如这个帖子的内容。 下面再载入模板,就像我们上面看到的分析一样,那个第三步就相当于把那个模板页面所有内容放到这个forumdisplay页面的最下面 当然,这个包含进来的模板是经过编译的,也就是把那个特有我们写的模板转变成PHP和HTML混合的代码。 所以最后我们得到的这个PHP页面里究竟变成了什么了呢? 有童鞋说出来了,这样得到的不就是一个混合了HTML和PHP代码的原生态写法的php文件吗? 对的。。。模板编译的结果就是最后给服务器运行一个原生态写法的php文件。

由于discuz模板编译的过程,是把templates文件下里对应的模板文件转变成原生态的写法
然后存放在cache的文件夹里,然后再包含进根目录下的文件里
所以大家也可以在缓存文件夹里的模板文件夹里查看到某个页面经过编译的原生态的php代码。

上面说了,经过模板编译的过程,把templates里面的htm文件变成原生态写法的php文件 那么我们可以肯定,上面的template函数肯定经过了一些特殊处理,把一定格式的htm文件转变成php文件 而这个htm文件一定要遵循某些符合这个函数转变规律的写法才行,否则就会编译错误。 而这个写法也就是我们这堂课的重点了。
既然要知道编译过程,我们首先还得来研究一下这个template函数 打开global.func.php,global是全局的意思,这个文件里有所有用途频繁的函数,而DZ命名也很有规律,比如这个里面都是函数,所以是func,如果里面有类呢?就是class了,大家有兴趣自己看看是不是? 代码如下:
  1. function template($file, $templateid = 0, $tpldir = '') {
  2. global $inajax, $hookscript;
  3. if(strexists($file, ':')) {
  4. list($templateid, $file) = explode(':', $file);
  5. $tpldir = './plugins/'.$templateid.'/templates';
  6. }
  7. $file .= $inajax && ($file == 'header' || $file == 'footer') ? '_ajax' : '';
  8. $tpldir = $tpldir ? $tpldir : TPLDIR;
  9. $templateid = $templateid ? $templateid : TEMPLATEID;
  10. $tplfile = DISCUZ_ROOT.'./'.$tpldir.'/'.$file.'.htm';
  11. $filebak = $file;
  12. $file == 'header' && CURSCRIPT && $file = 'header_'.CURSCRIPT;
  13. $objfile = DISCUZ_ROOT.'./forumdata/templates/'.STYLEID.'_'.$templateid.'_'.$file.'.tpl.php';
  14. if($templateid != 1 && !file_exists($tplfile)) {
  15. $tplfile = DISCUZ_ROOT.'./templates/default/'.$filebak.'.htm';
  16. }
  17. @checktplrefresh($tplfile, $tplfile, filemtime($objfile), $templateid, $tpldir);
  18. return $objfile;
  19. }
复制代码
大家可以分析出,这里的作用是如果有,找到其在templates文件夹里的模板和缓存文件夹里的对应编译好了的文件,通过checkplrefresh函数处理,然后返回编译好的文件。 然后看看里面出现的checkplrefresh函数:
  1. function checktplrefresh($maintpl, $subtpl, $timecompare, $templateid, $tpldir) {
  2. global $tplrefresh;
  3. if(empty($timecompare) || $tplrefresh == 1 || ($tplrefresh > 1 && !($GLOBALS['timestamp'] % $tplrefresh))) {
  4. if(empty($timecompare) || @filemtime($subtpl) > $timecompare) {
  5. require_once DISCUZ_ROOT.'./include/template.func.php';
  6. parse_template($maintpl, $templateid, $tpldir);
  7. return TRUE;
  8. }
  9. }
  10. return FALSE;
  11. }
复制代码
这个函数的作用是检测已经存在模板缓存文件是否过期或者被强制更新,如果需要重新编译,则导入专门处理模板的文件/include/template.func.php,然后使用parse_template函数,对这个模板进行处理。

我们来看看parse_template函数,其实看名字也知道,这个才是真正的重中之重,分析这个里面的规律就可以知道其编译的规律了。
我们看看代码:

  1. function parse_template($tplfile, $templateid, $tpldir) {
  2. global $language, $subtemplates, $timestamp;
  3. $nest = 6;
  4. $basefile = $file = basename($tplfile, '.htm');
  5. $file == 'header' && CURSCRIPT && $file = 'header_'.CURSCRIPT;
  6. $objfile = DISCUZ_ROOT.'./forumdata/templates/'.STYLEID.'_'.$templateid.'_'.$file.'.tpl.php';
  7. if(!@$fp = fopen($tplfile, 'r')) {
  8. dexit("Current template file './$tpldir/$file.htm' not found or have no access!");
  9. } elseif($language['discuz_lang'] != 'templates' && !include language('templates', $templateid, $tpldir)) {
  10. dexit("<br />Current template pack do not have a necessary language file 'templates.lang.php' or have syntax error!");
  11. }
  12. $template = @fread($fp, filesize($tplfile));
  13. fclose($fp);
  14. $var_regexp = "((\$[a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*)([[a-zA-Z0-9_-."'[]$x7f-xff]+])*)";
  15. $const_regexp = "([a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*)";
  16. $headerexists = preg_match("/{(sub)?templates+header}/", $template) || $basefile == 'header_ajax';
  17. $subtemplates = array();
  18. for($i = 1; $i <= 3; $i++) {
  19. if(strexists($template, '{subtemplate')) {
  20. $template = preg_replace("/[nrt]*{subtemplates+([a-z0-9_:]+)}[nrt]*/ies", "stripvtemplate('\1', 1)", $template);
  21. }
  22. }
  23. $template = preg_replace("/[nrt]*{csstemplate}[nrt]*/ies", "loadcsstemplate('\1')", $template);
  24. $template = preg_replace("/([nr]+)t+/s", "\1", $template);
  25. $template = preg_replace("/<!--{(.+?)}-->/s", "{\1}", $template);
  26. $template = preg_replace("/{langs+(.+?)}/ies", "languagevar('\1')", $template);
  27. $template = preg_replace("/{faqs+(.+?)}/ies", "faqvar('\1')", $template);
  28. $template = str_replace("{LF}", "<?="\n"?>", $template);
  29. ………………
复制代码
这个里面是不是很多正则式匹配的过程啊,还有很多的preg_replace,当然。。。这里就是把htm里面的所有代码进行一次一次按规律把HTML和专用语法代码转变成标准的HTML+PHP代码的过程了。 替换次数很多,所以上面的代码省略了,有兴趣可以点击这里查看: http://discuzdeveloper.googlecod ... e/template.func.php 比如我们在模板里写入这句:
  1. <h2>{$array[a]}</h2>
复制代码
经过处理,就会变成这样的原生态写法:
  1. <h2><?php echo $array['a']; ?></h2>
复制代码
上面的在php服务器上不能运行,而下面的经过编译了,就可以直接运行了。 在PHP模板里我们还能写入PHP语句的,当然这个也要经过编译才能变成直接运行的php代码 比如if,else等,还有循环loop,用来变成foreach。 这些在模板里作用很多,比如一个tab页面,大多是通过地址传值,然后经过IF等处理后,展示出来。 这些固定的写法是必须背诵的,下面我会详细介绍,其实也是这个文里面最重要的部分了。
看了上面的部分,同学们已经了解了整个模板在DISCUZ中的处理过程了。 那么我们来制作模板,知道这个原理后,最重要的可能就是要了解他们的语法了 然后才好自己随心所欲的编写模板。 我总结了一下,下面会分条列出: 1,首先要说的是变量,我们从上面的分析可以知道,在根目录文件里,首先已经把变量都赋值好了 在模板里直接应用就可以了,而这个变量在DISCUZ模板里要这样写:
  1. {$a},{$a[b]},{AAA}
复制代码
这里分别代表了变量,数组中某个值,常量的写法。 他们相当于:<?php echo $a ?>等语法。

2,模板语法。在模板里也能运用语法,这样可以判断然后展示出想要的模板样式。
首先说一下他们的通用写法,都要写在这个里面:

  1. <!--{}-->
复制代码
大家看这个是不是很熟悉啊,当然,细心的同学,我们在上一节课里已经说了注释的标示是<!----> 在这里多了一个{}然后里面就能写判断等语句了,写法如下:
  1. <!--{if}--><!--{/if}-->
  2. <!--{if}--><!--{else}--><!--{/if}-->
  3. <!--{if}--><!--{elseif }--><!--{else}--><!--{if}-->
复制代码
比如:
  1. <!--{if $userid}-->欢迎您<!--{else}-->请登陆<!--{/if}-->
复制代码
这就是一个简单的模板判断语句,判断用户时候有登陆。

3,循环语句。这个东西太重要了。大家务必掌握。

  1. <!--{loop $array $key $value)}--> <!--{/loop}-->
复制代码
这个句法相当于
  1. foreach($array as $key=>$value) {
  2. }
复制代码
作用就是把数组遍历输出。 里面的$array就是数据,可以是一维数组或者多维,多维的情况下可以使用$key[][]等方式展示出来。 这里的$key不一定要写出来,不写出来的时候,相当于直接遍历值而不用考虑键名。 比如首页的用户列表,帖子列表神马的都是把信息放入数组,然后在模板里遍历输出。

同学们看到这里可能有疑问,为啥米他们要用foreach而不用for或者while呢?
这个问题你们怎么不去问DZ开发人员?
不过如果让我来写,我也会用foreach的 ,原因不明。。。

4,
载入语言包,他们很复杂的把所有的中文写到了语言包里去了。这样有一定的好处,不过吾觉得实在是太麻烦了。

5,
载入另外一个模板。

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

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

  1. $template = str_replace("{LF}", "<?="\n"?>", $template);
复制代码
8,{subtemplate } 这个也是载入,不过不会对载入的进行处理,可以理解为直接引用。一般在做边栏模块的时候用的多。

9,
专门载入CSS文件的,因为在CSS文件中,我们会发现那个CSS也是htm结尾的,而且里面到底是使用哪个文件夹里的背景图片等等也是需要经过编译的。

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

好了,上面这些是要背诵的。请各位迅速记忆背诵,明天抽背,没背出来的罚抄100遍并且要请家长。

课堂练习:
  1. {subtemplate header}
  2. <div id="nav"><a href="$indexname">$bbname</a> &raquo; {lang home}</div>
  3. <!--{if $admode && !empty($advlist['text'])}--><div class="ad_text" id="ad_text"><table summary="Text Ad" cellpadding="0" cellspacing="1">$advlist[text]</table></div><!--{else}--><div id="ad_text"></div><!--{/if}-->
  4. <div id="wrap"{if $infosidestatus['allow'] < 2}{else}{/if}>
  5. <!--{if $infosidestatus[allow] == 2}-->
  6. <a id="sidebar_img" href="javascript:;" onclick="sidebar_collapse(['{lang sidebar_collapsed_yes}', '{lang sidebar_collapsed_no}']);" class="$collapseimg[sidebar]"><!--{if $collapseimg[sidebar] == 'collapsed_yes'}-->{lang sidebar_collapsed_yes}<!--{else}-->{lang sidebar_collapsed_no}<!--{/if}--></a>
  7. <!--{elseif $infosidestatus[allow] == 1}-->
  8. <a id="sidebar_img" href="javascript:;" onclick="sidebar_collapse(['', '{lang sidebar_collapsed_no}']);" class="collapsed_yes">{lang sidebar_collapsed_yes}</a>
  9. <!--{/if}-->
  10. <div class="main"><div class="content">
  11. $pluginhooks[index_header]
  12. {template index_header}
  13. <!--{if $indexhot['status']}-->
  14. {subtemplate index_heats}
  15. <!--{/if}-->
  16. $pluginhooks[index_hot]
  17. <div>
  18. <p>
  19. {lang index_today}: <em>$todayposts</em>, {lang index_yesterday}: <em>$postdata[0]</em>, {lang index_members}: <em>$totalmembers</em>
  20. </p>
  21. <!--{if $indextype}-->
  22. {subtemplate index_navbar}
  23. <!--{/if}-->
  24. </div>
复制代码
请大家联系把上面的代码翻译成原生态代码。

这个教程是以discuz7为基础的,但是X的模板编译系统貌似没有变化,所以是通用的。
好了,本堂课结束了。我们的基础知识部分也完成了。
从下一堂课开始,我们将正式进入如何手把手叫你做模板的过程了~~~
我们的目标是:能够随心所欲地模仿任何风格。能够彻彻底底地把内心想要的风格制作出来。
而且我相信,只要你切实把本老师的课程扎扎实实地学习好了,你一定能成为无敌的高手的(心里有点心虚哈~~)

DISCUZ是一个在技术上接近无敌的程序,
很多人都说,DZ官方怎么不出好看的模板啊,PW又如何如何,却对这套完整的规范的开发模板的系统视而不见。
而世上真正的好程序界面都是简陋的,比如wordpress,因为他们把皮肤制作功能规范化,而且完全开放给用户了。
谁又会稀罕那几套官方模板呢?

除了努力奋斗,我们还可以做什么?
discuz的模板制作与源代码分析
  • 文章目录
  • 站点概览
ken

ken

记录精彩的程序人生

498 日志
9 分类
77 标签
RSS
Creative Commons
Links
  • 酷壳
0%
© 2010 — 2025 ken
由 Halo 强力驱动
鄂ICP备18013899号-1