discuz模板機制

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 = '') { 
    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中,否則的話還是直接返回一個已經編譯好的模板文件。

我們來了解一下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">
    <html xmlns="http://www.w3.org/1999/xhtml" <?php language_attributes(); ?>>
    <head>
    <title><?php bloginfo('name'); ?> &rsaquo; <?php echo $title; ?></title>
    <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("/^(\xef\xbb\xbf)/", function($r){}, $fileContent);
        $fileContent=preg_replace_callback(
            "/\<\!\-\-\s*\\\$\{(.+?)\}\s*\-\-\>/is",
            function($r)
            {
                return str_replace('\"', '"', '<?php echo ' . $r[1] . '; ?>');
            },
            $fileContent
        );

        $fileContent=preg_replace_callback(
            "/\{(\\\$[a-zA-Z0-9_\[\]\\\ \-\'\,\%\*\/\.\(\)\>\'\"\$\x7f-\xff]+)\}/s",
            function($r)
            {
                return str_replace('\"', '"', '<?php echo ' . $r[1] . '; ?>');
            },
            $fileContent
        );
        $fileContent = preg_replace_callback(
            "#<!--\s*{\s*include\s+([^\{\}]+)\s*\}\s*-->#i", 
            function($r)
            {
                return '<?php include template("'.$r[1].'");?>';
            },
            $fileContent
        );

.........

        file_put_contents($cFile,$fileContent);

        return true;

    }

這個裏面是不是很多正則式匹配的過程啊,還有很多的preg_replace,當然。。。這裏就是把htm裏面的所有代碼進行一次一次按規律把HTML和專用語法代碼轉變成標準的HTML+PHP代碼的過程了。
替換次數很多,所以上面的代碼省略了。
比如我們在模板裏寫入這句:

<h2>{$array[a]}</h2>

經過處理,就會變成這樣的原生態寫法:

<h2><?php echo $array['a']; ?></h2>

上面的在php服務器上不能運行,而下面的經過編譯了,就可以直接運行了。
在PHP模板裏我們還能寫入PHP語句的,當然這個也要經過編譯才能變成直接運行的php代碼,比如if,else等,還有循環loop,用來變成foreach。這些在模板裏作用很多,比如一個tab頁面,大多是通過地址傳值,然後經過IF等處理後,展示出來。這些固定的寫法是必須背誦的,下面我會詳細介紹,其實也是這個文裏面最重要的部分了。

看了上面的部分,同學們已經瞭解了整個模板在DISCUZ中的處理過程了。那麼我們來製作模板,知道這個原理後,最重要的可能就是要了解他們的語法了。
然後纔好自己隨心所欲的編寫模板。
我總結了一下,下面會分條列出:
1,首先要說的是變量,我們從上面的分析可以知道,在根目錄文件裏,首先已經把變量都賦值好了。在模板裏直接應用就可以了,而這個變量在DISCUZ模板裏要這樣寫:

{$a},{$a[b]},{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 }
這個不用介紹了吧。不過這個你也能使用到的話,只能說明你要麼是白癡要麼是天才。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章