立即注册 登录
彼岸网 返回首页

天香公主的个人空间 http://www.bian-wang.com/discuz/?10005 [收藏] [复制] [分享] [RSS] https://github.com/txgz999/discuz/wiki

日志

分离出Discuz模板机制的尝试

热度 1已有 1457 次阅读2018-5-26 11:05 AM

本文总结下近期和网友carry0987的讨论:如何将Discuz的模板机制从Discuz系统里剥离出来,用于任何PHP网站上去。这事过去就有人做过,找到 Akon(番茄红了)十年前写的"Discuz 模板类下载 分离自Discuz"http://www.21andy.com/new/20180305/998.html,但是因为源于较早版本的Discuz,所以不支持PHP7。所以我从Discuz3.4版里又重新将与模板机制有关的代码剥离里一遍。

Discuz开发组的lengke讨论过Discuz的模板机制 http://www.cnblogs.com/lengke/archive/2009/12/07/1618840.html,他说:
我们需要模板来做什么? 分离程序与界面,为程序开发以及后期维护提供方便。

在Discuz里提供模板机制支持的文件主要是两个:
1) source/function/function_core.php
它包含了一个名叫函数template,这是模板机制的入口。脚本文件通过调用这个函数来使用模板:
include_once template("home/spacecp_blog"); 这个函数被调用时会检查该模板的缓存文件是否存在,源文件是缓存文件生成后是否有过改动。如果有必要的话,它会调用下面说的template类来生成缓存文件。
2) source/class/class_template.php
解析模板源文件来产生缓存文件的工作是由这个文件里的template类实现的。其中的parse_template函数是模板机制的主干。它解析了所支持的各种模板标签,将它们的内容转化成PHP代码放在缓存文件里。

所以我做的主要工作就是从Discuz的这两个文件里拷贝出与模板有关的代码,也去掉了些只在Discuz环境下适用的代码。剥离出的代码支持下面的模板标签:
支持的标签不支持的标签
  • block
  • echo
  • eval
  • if/elseif/else
  • lang
  • loop
  • template
  • subtemplate
  • ad
  • avatar
  • block/1, block/2, et al
  • blockdata/1, blockdata/2, et al
  • csstemplate
  • date
  • hook


下面着重介绍下我的程序与原来模板机制用法的细微不同之处:

1. 添加css标签
Discuz支持一个叫csstemplate的标签,它的作用是从一个名叫module.css的CSS母版文件里生成各个网页所需的风格部分的CSS文件。这是个很好的功能。但是在Discuz外我们需要一个灵活而简单的办法在模板里加上现成的CSS文件。这就是按carry0987的建议加上的css标签。和直接在模板里用html的语法加CSS文件的做法相比,它产生的CSS文件url带着timestamp,而且当这个CSS文件被改动后,模板缓存文件会自动更新这个路经,所以用户浏览器就会下载更新后的css文件。

2. 有关lang标签
虽然Discuz模板支持使用语言包,但是并不让单个模板文件指明使用哪些个语言包文件,而是设立了模板可以使用哪些语言包文件的规则:它可以使用语言包根目录里的lang_template.php文件,以及下一层与该模板文件所在的模板文件根目录的第一层子目录同名的目录里的lang_template.php文件。我的程序也沿用了这个规则,但是允许每一层都提供lang_template.php文件。例如在模板根目录下的模板文件level1/level2/level3/test.htm 可以使用四个语言包根目录下的下列四个语言包文件:lang_template.php;level1/lang_template.php;level1/level2/lang_template.php 和 level1/level2/level3/lang_template.php。另外在Discuz系统里语言包文件如果变了,Discuz并不会更新用到它的缓存文件。只有去管理中心里点击更新缓存按钮,变动后的语言包内容才会显示在网页上。在我的程序里当语言包文件被改动后,模板缓存文件会自动更新(在下节详述)。

3. 自动检测和使用更新的CSS内容和语言包内容
在一定设置下(在设置文件里将tplrefresh设为1),Discuz在使用模板前会用文件最后修改时间来判断缓存文件生成后,源文件是否有变化。如果发现源文件更新的话,就会重新生成并使用缓存文件。但是子模板的内容是嵌入进使用它的模板里的,如果单单子模板更新了,主模板如何能得知它需要嵌入新的子模板内容呢? Discuz想到了这点,在(主)模板缓存文件的头部加上了对它包含的各个子模板文件是否改动过的检查。如果发现任何一个子模板文件在(主)模板缓存文件生成后有改动,就重新生成这个缓存文件。由于该缓存文件的内容已经被放在内存上在执行,新的缓存文件保存在硬盘上。所以这次执行的还是旧的缓存文件,新的缓存文件要等下次用到该模板时才会被使用。事实上有关子模板的更新办法同样适合于模板用到的其它资源:CSS文件和语言包文件。所以我的程序就是按这个办法在CSS文件和语言包文件有变动时自动更新主模板缓存的。
要想在模板用到的那些资源有变动时,立刻就更新和使用新的缓存文件的话,我们需要把这模板依赖哪些资源文件这个信息单独存放(放在数据库里或用对每个模板用一个单独的文件来记录这些信息),在使用缓存文件之前,先检查那些文件是否有变,如果有,先更新缓存文件再执行。

下载链接: http://www.bian-wang.com/discuz/data/userupload/10005/dztemplate.zip
05/31/2018 更新:加了js标签

发表评论 评论 (16 个评论)

回复 carry0987 2018-6-23 07:10 AM
天香公主: 不知你的留言板是如何决定使用哪种语言?
一開始為英文,而主頁的footer有個選單,在其中選擇語言後會將language=zh_cn這類的文字寫入cookie,接著class_core.php便會依照cookie中的語言選擇要include的檔案,例如cookie中的language是zh_cn, 那麼class_core.php便會從language文件夾內引入zh_CN.php這個檔案,而檔案內容就只是
<?php
$lang = array(
    'Hello' => '您好',
);
?>
回复 天香公主 2018-6-23 07:04 AM
carry0987: 我研究出一個結論了,如果要像我的留言板那樣支援多國語言,那麼lang這種函數是不可能的,因為如果把$lang陣列的內容寫入緩存,那麼一但有多個使用者選擇不同的 ...
不知你的留言板是如何决定使用哪种语言?
回复 carry0987 2018-6-23 06:52 AM
天香公主: 我不知道理解得对不对,你有个下述内容的PHP文件
------------------------------------------------------------------
<?php
$var = 'Success';
$lang = array ...
我研究出一個結論了,如果要像我的留言板那樣支援多國語言,那麼lang這種函數是不可能的,因為如果把$lang陣列的內容寫入緩存,那麼一但有多個使用者選擇不同的語言,例如:
en.php >> Hello {$user},welcome to here!
cn.php >> {$user} 您好,歡迎光臨本站
那麼將會出現衝突,template class 會不知道該寫入哪個到緩存中,當然更不可能根據不同的語言產生多個緩存,因此,lang的做法或許不適合多國語言的站點...
回复 carry0987 2018-6-19 08:49 AM
天香公主: 我不知道理解得对不对,你有个下述内容的PHP文件
------------------------------------------------------------------
<?php
$var = 'Success';
$lang = array ...
我弄懂我的問題由來了,我的留言板是採用分離式的語言包架構,我把語言文件都放在./language裡面,分別是 en_US.php, zh_TW.php, ja_JP.php,而我的引入語言文件的函數是在這個模板class以外的地方,所以才導致無法像你的模板引擎那樣,直接將語言包內的文字內容寫入tpl.php的緩存文件內,造成現在這種情況

P.S. 說得更簡單一點,就是我的緩存文件內的語言調用的部份,是顯示成
<?php echo $lang['one_day']; ?>
這樣的
回复 天香公主 2018-6-18 11:32 PM
carry0987: 天香,我這裡有個問題,
假如我令 $var = 'Success';
然後使用了
$lang = array('test' => 'Show {$var}');
作為language,
然後用
    public function language ...
我不知道理解得对不对,你有个下述内容的PHP文件
------------------------------------------------------------------
<?php
$var = 'Success';
$lang = array('test' => 'Show {$var}');

function languageVar($var)
{
    $var = preg_replace("/\{(\\\$[a-zA-Z0-9_\-\>\[\]\'\"\$\.\x7f-\xff]+)\}/s", "<?php echo \\1;?>", $var);
    return $var;
}
?>

<?php echo languageVar($lang['test']) ;?>
------------------------------------------------------------------
运行后显示的结果是
    Show
而你希望显示
    Show Success
对吗?可以将第二句改为
    $lang = array('test' => "Show {$var}");
回复 carry0987 2018-6-18 06:18 AM
天香,我這裡有個問題,
假如我令 $var = 'Success';
然後使用了
$lang = array('test' => 'Show {$var}');
作為language,
然後用
    public function languageVar($var)
    {
        $var = preg_replace("/\{(\\\$[a-zA-Z0-9_\-\>\[\]\'\"\$\.\x7f-\xff]+)\}/s", "<?php echo \\1;?>", $var);
        return $var;
    }
的函數,
接著以
<?php echo languageVar($lang['test']) ;?>
顯示出
Show success
的效果,但現在卻只顯示出Show...
回复 天香公主 2018-6-13 10:25 AM
carry0987: 對了,html一旦改動,刷新一次就會更新,不過css與js的部分要刷新兩次,我估計是因為沒有即時讀取模板緩存中的hash,我試著改改 ...
>> 不過css與js的部分要刷新兩次

对,我文中的最后一段说的就是这事
回复 carry0987 2018-6-13 08:22 AM
對了,html一旦改動,刷新一次就會更新,不過css與js的部分要刷新兩次,我估計是因為沒有即時讀取模板緩存中的hash,我試著改改
回复 carry0987 2018-6-7 03:30 AM
天香公主: 关于1),你的意思是我的代码在第一次使用时会出现错误?我的测试结果是在第一次使用时会自动生成缓存文件,因为那时在函数 template($file, $gettplfile = 0)  ...
關於1的修改,我是因為將所有的錯誤抑制運算符@都去除了,所以變成以file_exists判定文件是否存在

關於2的修改,我這裡重新下了你的dztemplate並上傳後,發現是因為我把
$template = preg_replace_callback("/[\n\r\t]*\{echo\s+(.+?)\}[\n\r\t]*/is", array($this, 'parse_stripvtags_echo1'), $template);

這句代碼的位置移動到上面,造成解析的順序錯誤,才導致這個問題的
回复 天香公主 2018-6-4 11:07 PM
carry0987: 我做了2個修改 :
1. 基於 function template 在第一次使用時會因為尚未生成緩存文件而找不到,我加入 file_exists 用以判斷檔案是否存在
https://gist.github.c ...
关于1),你的意思是我的代码在第一次使用时会出现错误?我的测试结果是在第一次使用时会自动生成缓存文件,因为那时在函数 template($file, $gettplfile = 0) 里到达
    checktplrefresh($tplfile, $tplfile, @filemtime(DISCUZ_ROOT.$cachefile), $cachefile, $file);
时第三个变量的值是 false,所以条件
    if(empty($timecompare) || @filemtime(DISCUZ_ROOT.$depfile) > $timecompare)
必然满足,所以会生成缓存文件。

关于2),这句在我的文件demo.htm里吧,我查了产生的demo.tpl.php里对应的代码是
    <?php echo count($testArr) ; ?>  
这是预期的结果啊。

你测试的是我新贴的dztemplate.zip, 还是在改善原来的discuztemplate.zip?两者在结构上有些细微的差别。起初我是在Akon的代码产生的discuztemplate.zip,但后来觉得直接从Discuz3.4里分离代码更容易,dztemplate.zip是这样产生的。
回复 carry0987 2018-6-4 10:40 AM
我做了2個修改 :
1. 基於 function template 在第一次使用時會因為尚未生成緩存文件而找不到,我加入 file_exists 用以判斷檔案是否存在
https://gist.github.com/carry0987/bf163cb472335a9147504b29582e4762

2. 因為使用<!--{echo count($testArr) }-->時會因為正則而變成
<?php echo count(<?=$testArr ;?>) ;?>
我加入了 \{ \} 作為區別普通的變量輸出
https://gist.github.com/carry0987/537341e40c0d27fd0680810117f22e8b
回复 天香公主 2018-5-31 09:46 PM
carry0987: 對了,天香,在 stripscriptamp 這個函式中的 type=\&quot;text/javascript\&quot; 建議去除,避免重複出現~
嗯,我把这段删掉了,另外加了一个名叫 js 的标签。
回复 carry0987 2018-5-31 07:47 AM
對了,天香,在 stripscriptamp 這個函式中的 type=\"text/javascript\" 建議去除,避免重複出現~
回复 carry0987 2018-5-31 07:30 AM
天香公主: 一种在模板文件里给PHP变量赋值的语法,较适用于值是HTML内容的情形。比如

{block a}
<div>Test</div>
{/block}

等同于

$a=&quot;<div>Test</div>&quot;; ...
剛剛看了此帖,已經明白了
http://www.discuz.net/forum.php?mod=viewthread&tid=3298935
回复 天香公主 2018-5-31 07:07 AM
carry0987: 研究完成啦! 有個問題想問一下,block究竟是?
一种在模板文件里给PHP变量赋值的语法,较适用于值是HTML内容的情形。比如

{block a}
<div>Test</div>
{/block}

等同于

$a="<div>Test</div>";
回复 carry0987 2018-5-31 03:53 AM
研究完成啦! 有個問題想問一下,block究竟是?

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 立即注册

小黑屋|Archiver|彼岸网  

Powered by Discuz! X3.1 © 2001-2014 Comsenz Inc.
GMT-4, 2024-3-19 01:47 AM , Processed in 0.021339 second(s), 7 queries. ,ApcOn

返回顶部