【转】从PHP的模板引擎看Discuz!模板机制
发布时间:2016-03-23, 10:53:14 分类:PHP | 编辑 off 网址 | 辅助
正文 2974字数 525,262阅读
前言
本文写于2007年11月,那时候我是在Discuz!开发组为PHPChina的《PHPer》写的稿,一直也没有发到blog上了,今天偶然之间记起,顺手转发过来。
本文写于2007年11月,那时候我是在Discuz!开发组为PHPChina的《PHPer》写的稿,一直也没有发到blog上了,今天偶然之间记起,顺手转发过来。
一、关于模板引擎的前言
从phplib到smarty,再到Discuz!的模板机制,本文试图通过PHP模板引擎为你讲解作者自己的PHP心得。
我清楚的记得在我刚上大学开始学习PHP的时候,曾经在phpe.net看到过一篇关于phplib Template和FastTemplate这两模板引擎性能比较的文章。让我在接下来半年的时间内持续的使用着phplib。不可否认phplib是左右了一代PHP开发人员对于PHP模板引擎的认识。或许你也会对下面的方法比较熟悉
$t->set_file
$t->set_var
Run code
Cut to clipboard
当我对于phplib的执行效率不满意的时候,我开始寻找下一个PHP的模板引擎,于是smarty跳入我的视野范围,当我费尽心血去学会了smarty并使用开发了很多东西,而现在的我突然发现记得的也就只有下面的方法了
$s->assign
$s->display
Run code
Cut to clipboard
究竟我们需要模板引擎来做什么呢,MVC?简单?易用?效率?请看下文的分析。
二、程序处理的分析
1.PHPLIB的程序处理过程
从phplib的处理开始讲起
$t = new Template()
$t->set_file
$t->set_var
$t->parse
$t->p
Run code
Cut to clipboard
看上面的代码,翻译成中文就是
初始化模板类$t
设置模板文件
设置模板变量
分析模板文件中的模板变量
输出内容
通过了最少5个步骤在php程序中实现模板的处理
2.Smarty的程序处理过程
现在来看smarty的处理
$s = new Smarty
$s->assign
$s->display
Run code
Cut to clipboard
翻译成中文就是
初始化模板类$s
设置模板变量
解析并输出模板
3.Discuz!模板的程序处理过程
include template(tplname);
Run code
Cut to clipboard
主要作用就是指定给程序需要处理的模板文件
在上述三种模板处理机制中,最容易理解和接受就是Discuz!模板的处理过程。初始化、设置变量、解析模板、输出内容,Discuz!只用了一个函数来做。对于一个开源的论坛软件,这样处理的好处是显而易见的,对于Discuz!进行二次开发的程序员的要求降低。简化模板语言,方便风格和插件的制作,这也在一定程度上促进了Discuz!的传播
三、模板源文件的语法
在phplib中处理循环嵌套的时候,使用:
<!-- BEGIN row -->
{it}
<!-- END row -->
Run code
Cut to clipboard
在smarty中处理循环嵌套的时候,引入了
< {section name=loopName loop=$loopArray}>(当然还有foreach这样的)
Run code
Cut to clipboard
在Discuz!中处理循环嵌套的时候,
<!--{loop $array $value}-->
Run code
Cut to clipboard
其实真正的模板面对的可以说是不懂PHP或者懂一点PHP的美工同志们,模板的复杂就意味着美工制作页面的难度加大。在必不可少的需要模板有逻辑处理的时候,为什么不在html代码中使用原生态的PHP语法,而让美工相当于去学习另外一种语言呢?在我个人的经验中,显然是Discuz!的模板语言更为简单易学,也为我节省了更多的时间。
四、Discuz!模板处理机制
我剥离出一个简单的Discuz!模板处理函数
function template($file, $templateid = 0, $tpldir = '') {
$tplfile = DISCUZ_ROOT.'./'.$tpldir.'/'.$file.'.htm';//模板源文件,此处$tplfile变量的值可能是D:\discuz\templates\default\demo.htm
$objfile = DISCUZ_ROOT.'./forumdata/templates/'.
$templateid.'_'.$file.'.tpl.php';//模板缓存文件,此处$objfile变量的值可能是D:\discuz\forumdata\templates\1_demo.tpl.php
//如果模板源文件的修改时间迟于模板缓存文件的修改时间,
//就是模板源文件被修改而模板缓存没有更新的时候,
//则调用parse_template函数重新生成模板缓存文件。
if(@filemtime($tplfile) > @filemtime($objfile)) {
require_once DISCUZ_ROOT.'./include/template.func.php';
parse_template($file, $templateid, $tpldir);
}
//返回缓存文件名称
//$objfile变量内容可能为D:\discuz\forumdata\templates\1_demo.tpl.php
return $objfile;
}
Run code
Cut to clipboard
而php页面的模板执行语句
include template('demo');
Run code
Cut to clipboard
实际上在本例中就是相当于
include 'D:\discuz\forumdata\templates\1_demo.tpl.php';
Run code
Cut to clipboard
这个流程就是一个demo.php文件中当数据处理完成以后include template('demo'),去显示页面。
五、总结
我也曾经看到过有列举出很多种的PHP模板引擎,但是我觉着phplib、smarty、Discuz!模板机制就足以说明问题了。
1.我们需要模板来做什么?
分离程序与界面,为程序开发以及后期维护提供方便。
2.我们还在关心什么?
PHP模板引擎的效率,易用性,可维护性。
3.最后的要求什么?
简单就是美!
我的文章好像没有写完,其实已经写完了,我要说明的就是从PHP的模板引擎看Discuz!模板机制。分析已经完成,或许以后我会再写篇实际数据的测试供给大家参考!
原文 http://www.ccvita.com/288.html
Run code
Cut to clipboard
(支付宝)给作者钱财以资鼓励 (微信)→
有过 4 条评论 »
class_core.php是discuz 3.x的核心文件,几乎所有PHP脚本都有引用此文件初始化论坛运行环境。以下解析引用3.2版discuz。
line 12-15:常量定义
IN_DISCUZ: true //用于防止内部PHP引用文件被直接调用。 DISCUZ_ROOT: E:\\project\\discuz\\ //论坛所在的物理路径 DISCUZ_CORE_DEBUG: false //核心代码是否测试模式 DISCUZ_TABLE_EXTENDABLE: false //未知
line 17: 设置自定义异常处理功能
处理方法位于:core::handleException静态方法。
line 24-30:定义自动加载类函数 该方法位于:core::autoload($class)
if(function_exists('spl_autoload_register')) { spl_autoload_register(array('core', 'autoload')); //自动加载类函数 } else { function __autoload($class) { return core::autoload($class); } }
该方法简述:引用./source/class/文件夹下的类文件。方法参数为类名称,类名称如有_下划线,则前缀为子文件夹名称,后缀为文件夹名_类名。 ./source/class/子文件夹/子文件夹名_类 例:$class = "discuz_base",则引用类文件为: ./source/class/discuz/discuz_base.php 所有引用过的文件名都存储在core::imports数组内。
line 33:执行C::createapp()静态方法.
C::creatapp();
该方法为最重要的方法,是初始化论坛的方法,该方法创建discuz_application类对象,使用单一工厂模式。 discuz_application类位于: ./source/class/discuz/discuz_application.php
C::createapp()方法不直接创建discuz_application类实例,而是通过执行discuz_application::instance()静态方法间接创建。
discuz_application::instance()静态方法new一个实例,构造函数初始化了论坛环境。(详情请看discuz_application.php解析)。
另外,该文件结尾用简写重定义了类名: line 208-209: C重定义core类名; DB重定义discuz_database类名。
discuz_application.php解析:
该类位于./source/class/discuz/discuz_application.php,被core对象间接实例化。该类有两个最重要的方法,一个是构造函数,一个是init方法。
构造函数解析:
line 57-62:构造函数对论坛参数的初始化,分别用4个方法实施:
public function __construct() { $this->_init_env(); $this->_init_config(); $this->_init_input(); $this->_init_output(); }
(1)$this->_init_env():
初始化环境变量 line 87-93:常量定义 MAGIC_QUOTES_GPC:true(5.4版以上为false) ICONV_ENABLE:true MB_ENABLE:true EXT_OBGZIP:true TIMESTAMP:当前时间截 并设当前时区为格林尼治时区
line 94: 引用核心函数库:./cource/function/function_core.php。 引用成功并定义常量:DISCUZ_CORE_FUNCTION:true
line 99-104:设置ini:memory_limit:128M line 106:检测爬虫:IS_ROBOT:false
line 108-112:清除不必要的全局变量。
foreach ($GLOBALS as $key => $value) { if (!isset($this->superglobal[$key])) { $GLOBALS[$key] = null; unset($GLOBALS[$key]); } }
line 114-203:定义全局变量:$_G,对象本身的var属性也保存一份其引用。
(2)$this->_init_config() 初始化环境:
line 289: 引用./conifg/config_global.php配置参数文件
line 299:检测并设定$_config['security']['authkey']的值。
if(empty($_config['security']['authkey'])) { $_config['security']['authkey'] = md5($_config['cookie']['cookiepre'].$_config['db'][1]['dbname']); }
line 303-315: 检测配置参数是否存在debug配置参数,如果没有则 定义常量DISCUZ_DEBUG:false,否则设为true。
line 316-317:常量定义:STATICURL:static/,则存入var属性。
line 319-320: 将所有$_config数组参数,存入:$this->config和$this->var['config']。
line 322: $_config['cookie']['cookiepath']参数值前加/斜线。
line 325: 重定义$this->var['config']['cookie']['cookiepre']值。
(3)$this->_init_input()初始化输入
line 236-240: 如果魔术引号功能开启,则去除$_GET,$_POST,$_COOKIE反斜线;
line 243-246: 根据$this->config['cookie']['cookiepre']值,将带有此前缀的cookie都存入$this->var['cookie'],key不带前缀。
line 251-253: POST值合并入GET内; GET值存入$this->var['gp_'.键名]中。
line 255-257:$_GET['page']url编码。
line 259-261: 处理掉无效的$_GET['handlekey'],该值只能含有字母数字和下划线。
line 264-268: 将$_GET值存入$this->var['gp_'.键名]中,所有值做addslashes引用(即引号和\作\引用)
line 270-273: 初始化$_GET['mod'],存入$this->var['mod'],mod参数为执行的模块名; 初始化$_GET['inajax'],存入$this->var['inajax'],该值判断请求是否ajax请求; 初始化$_GET['page'],存入$this->var['page']; 初始化$this->var['cookie']['sid'],存入$this->var['sid'],一般情况为空值。
line 275-278: 如果$this->var['cookie']['saltkey']不存在,则生成该值,并存入cookie,有效期1个月。
line 279: 根据$this->var['cookie']['saltkey']和$this->var['config']['security']['authkey'],生成$this->var['authkey']值。
(4)$this->_init_output() 初始化输出:
line 337-342:判断网页是否启用gzip压缩,设定$this->config['output']['gzip']布尔值;并决定设定$_G['gzipcompress']的布尔值。
if(!empty($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === false) { $this->config['output']['gzip'] = false; } $allowgzip = $this->config['output']['gzip'] && empty($this->var['inajax']) && $this->var['mod'] != 'attachment' && EXT_OBGZIP; setglobal('gzipcompress', $allowgzip);
line 344-346: 开启输出缓存。
if(!ob_start($allowgzip ? 'ob_gzhandler' : null)) { ob_start(); }
line 348-353: 设定$_G['charset']和常量CHARSET的字符编码值,取值于$this->config['output']['charset']。 将字符编码输出于网页头部。
setglobal('charset', $this->config['output']['charset']); define('CHARSET', $this->config['output']['charset']); //默认输出字符编码标识 if($this->config['output']['forceheader']) { @header('Content-Type: text/html; charset='.CHARSET); }
discuz_application::init()方法解析:
该方法也主要初始化论坛环境,准备好相关的数据库连接类、设置、用户、会话等等相关参数。
(1)$this->init_db():
private function _init_db() { if($this->init_db) { $driver = function_exists('mysql_connect') ? 'db_driver_mysql' : 'db_driver_mysqli'; if(getglobal('config/db/slave')) { $driver = function_exists('mysql_connect') ? 'db_driver_mysql_slave' : 'db_driver_mysqli_slave'; } DB::init($driver, $this->config['db']); } }
DB类位于./source/class/discuz_database.php,实际上就是discuz_database类。DB::init()根据参数配置实例化mysql连接类。
(2)