PTE : Pixmicat! Template-Embedded Library

起源及理念

在修改 GazouBBS雙葉改-MySQL scribe FIXED 時,開始醞釀出將討論串編排以樣版系統的方式呈現,剛好趁著 Experimental 實驗版的誕生,以 Smarty1 樣版系統開始試著加進去。但是 Smarty 需要引入的函式庫太大了,於是剛好在 Pixmicat! 開發相關討論 版面由 ◆9xnqOEff5Q 君提出了:

無標題 名稱: ◆9xnqOEff5Q [05/10/19(三)06:05 ID:N3ZWqi1U] No.233

> 另外樣板功能…Smarty?
雖然我跟Smarty比較熟,但是一個Template engine遠比主程式還要大的情形真的很奇怪耶 XD
除了AndCycle提到的FastTemplate以外,TinyButStrong這個也體積滿小的,可以考慮看看。

於是在 Experimental:02 版本正式推出了以 TinyButStrong (TBS)2 作為樣版系統的新架構。

然而首先面臨的是生成靜態頁面的效能問題。因為 TBS 在初始化後傳入資料印出後,其物件就無法再次被使用,變得使用方法是 生成→導入→輸出→回收→再生成 這樣的動作,而隨著生成頁數的增加,其效率拖慢會相當明顯。在結束了 Experimental 實驗版的試驗,邁入 Pixmicat! 更名同時,著手設計一套專供 Pixmicat! 使用的輕量型樣版系統,命名為 Pixmicat! 內嵌樣板函式庫(Template-Embedded Library) ,簡稱 PTE 。

PTE 只實裝了區塊重複、字串取代和 if 判斷式功能。並以 PCRE3 函式庫提供的快速函式為基礎,經過多次的效能測試撰寫出最佳化的程式碼。速度上遠勝過於 Smarty 及 TinyButStrong,檔案大小也是極小的 1.7KB,供 Pixmicat! 使用恰好。

新版PTE有大幅增強。改變如下:

  • 新版PTE不再限制區塊數目。也就是不再只有 &MAIN, &REPLY, &SEPARATE 三個區塊。
  • 新版PTE中一個區塊可以引用其他區塊。*1
  • 新版PTE中 &IF 指令可以引用其他區塊。*2
  • 新版PTE中新增 &FOREACH 迴圈指令。*3
  • 新版PTE可以判斷區塊值是否false(0,''為false, 其餘為true)

技術細節

使用方式

!!! 警告 !!!
所有未定義的變數將會原封不動回傳!
如果Template Driver是依賴未定義變數自動清空的行為的話,
將會有不可預測的行為出現!
<?php
include('lib_pte.php');
 
$PTE = new PTELibrary('inc_pixmicat.tpl'); // 分析樣板檔案 (*.tpl)
echo $PTE->ParseBlock('MAIN', array('{NO}'=>$no, ...)); // 排版資料輸出
echo $PTE->BlockValue('THEMEVER'); // 取出區塊值
?>

從其他樣版引擎移行至PTE

以freeStyle掲示板(HTML template4 版)為例,由於有性質上的差異*4,Template Driver (index.inc)會有小幅度的改變。

其中要把HTML template Argument Array轉換為PTE Argument Array需要以下代碼:

<?php
function toPTEargs($args) {
    $pteargs=array();
    foreach($args as $key => $val) {
        if(is_array($val)) $pteargs[is_int($key)?$key:'{$'.$key.'}']=toPTEargs($val);
        else $pteargs[is_int($key)?$key:'{$'.$key.'}']=$val;
    }
    return $pteargs;
}
?>

而起動用的代碼也有所改變。
由:

<?php
$pc_html = HtmlTemplate::t_buffer("$bbs_skin_pc", $arg);
?>

改為:
<?php
$pteargs=toPTEargs($arg);
$pc_PTE=new PTELibrary($bbs_skin_pc);
$pc_html=$pc_PTE->ParseBlock('MAIN',$pteargs);
?>

由:

<?php
HtmlTemplate::t_include($read_skin, $arg);
?>

改為:
<?php
$PTE = new PTELibrary($read_skin);
$pteargs = toPTEargs($arg);
echo $PTE->ParseBlock('READ',$pteargs);
?>


由於FOREACH中無法存取父區塊的變數,所以以下語法讓Template Driver中的變數有所改變:

例如:

(Template Code)

[loop oya]

{$oya/resnum}
[loop oya/res]
{$oya/key} {$oya/res/no}
[/loop]

[/loop]

(Driver Code)

<?php
        // レスHTML
        $resarr = compact('name', 'email', 'now', 'com', 'host', 'maillink', 'over', 'show');
?>

要改為:

(PTE Template Code)

<!--&MAIN-->
<!--&FOREACH($oya,'THREAD')-->
<!--/&MAIN-->

<!--&THREAD-->
{$resnum}
<!--&FOREACH($res,'RES')-->
<!--/&THREAD-->

<!--&RES-->
{$key} {$no}
<!--/&RES-->

(Driver Code)

<?php
        // レスHTML
        $resarr = compact('name', 'email', 'now', 'com', 'host', 'maillink', 'over', 'show', 'key');
?>

語法

定義區塊

語法:
<!--&(區塊名稱)--> <!--/&(區塊名稱)-->

範例:
<!--&MAIN-->
<div class="main">
...
</div>
<!--/&MAIN-->

一個 PTE 樣版引入的檔案包含了以下區塊:

BODYHEAD - <BODY>中的頁首
ERROR - 錯誤頁面
FOOTER - 頁尾
HEADER - HTML頁首
JSHEADER - HTML頁首中的Javascript
MAIN - 主版面 (討論串 + 刪除表單 + 換頁列)
POSTFORM - 發文表單
REPLY - 討論串回應
SEARCHRESULT - 搜尋結果
SEPARATE - 分隔線
THEMEAUTHOR - 主題風格作者名稱
THEMENAME - 主題風格名稱
THEMEVER - 主題風格版本
THREAD - 討論串首篇

取代字串

語法:
{$(字串名稱)}

範例:
名稱: {$NAME}

單純的字串取代,PTE 會將搜尋特定標籤並以資料取代。

目前字串有以下幾類:

{$ADDITION_INFO} - (發文表單用) config.php 中的表單下額外文字
{$ADMIN} - (BODYHEAD用) 「管理」連結
{$ALLOW_UPLOAD_EXT} - (發文表單用) config.php 中接受之附加圖檔副檔名
{$BACK_TEXT} - (錯誤頁面用) 「返回」字串
{$CATEGORY_TEXT} - (類別標籤分類功能) 「類別:」字串
{$CATEGORY} - (類別標籤分類功能) 文章的類別標籤列表
{$COM} - 文章內文
{$DEL_HEAD_TEXT} - 刪文表單的頭
{$DEL_IMG_ONLY_FIELD} - (刪文表單用) 「僅刪除附加檔案」勾選按鈕
{$DEL_IMG_ONLY_TEXT} - (刪文表單用) 「僅刪除附加檔案」字串
{$DEL_PASS_FIELD} - (刪文表單用) 刪除用密碼欄
{$DEL_PASS_TEXT} - (刪文表單用) 「刪除用密碼:」字串
{$DEL_SUBMIT_BTN} - (刪文表單用) 「刪除」按鈕
{$FOOTER} - 頁尾
{$FORMBOTTOM} - (發文表單用) 表單下的HTML碼
{$FORMTOP} - (發文表單用) 表單上的HTML碼
{$FORM_ATTECHMENT_FIELD}
{$FORM_ATTECHMENT_TEXT}
{$FORM_CATEGORY_FIELD}
{$FORM_CATEGORY_NOTICE}
{$FORM_CATEGORY_TEXT}
{$FORM_COMMENT_FIELD}
{$FORM_COMMENT_TEXT}
{$FORM_CONTPOST_FIELD}
{$FORM_CONTPOST_TEXT}
{$FORM_DELETE_PASSWORD_FIELD}
{$FORM_DELETE_PASSWORD_NOTICE}
{$FORM_DELETE_PASSWORD_TEXT}
{$FORM_EMAIL_FIELD}
{$FORM_EMAIL_TEXT}
{$FORM_NAME_FIELD}
{$FORM_NAME_TEXT}
{$FORM_NOATTECHMENT_FIELD}
{$FORM_NOATTECHMENT_TEXT}
{$FORM_NOTICE_NOSCRIPT}
{$FORM_NOTICE_STORAGE_LIMIT}
{$FORM_NOTICE}
{$FORM_SUBMIT}
{$FORM_TOPIC_FIELD}
{$FORM_TOPIC_TEXT}
{$HOME} - (BODYHEAD用) 「主頁」連結
{$HOOKLINKS} - (PMS用) (BODYHEAD用) PMS附加頁首連結
{$HOOKPOSTINFO} - (PMS用) (發文表單用) PMS發文表單下方附加說明文字
{$IMG_BAR} - (有貼圖時) 圖片資訊列,顯示長寬大小用
{$IMG_SRC} - (有貼圖時) 圖片本體
{$JS_REGIST_WITHOUTCOMMENT}
{$JS_REGIST_UPLOAD_NOTSUPPORT}
{$JS_CONVERT_SAKURA}
{$MAX_FILE_SIZE} - (發文表單用) 「最大檔案大小」字串
{$MESG} - (錯誤頁面用) 錯誤信息
{$NAME_TEXT} - (討論串用) 「名稱」字串
{$NAME} - 發文者名稱
{$NOW} - 發文時間
{$NO} - 文章編號
{$PAGENAV}
{$QUOTEBTN} - 文章引用系統功能按鈕 (可以按的 No.XXX)
{$REFRESH}
{$REPLYBTN} - (一般瀏覽時) 進入回應連結
{$RESTO}
{$RETURN_TEXT}
{$SEARCH} - (BODYHEAD用) 「搜尋」連結
{$SELF2}
{$SELF}
{$STATUS}
{$SUB} - 文章標題
{$THREADFRONT}
{$THREADREAR}
{$THREADS}
{$TITLE} - 標題
{$TOP_LINKS} - (BODYHEAD用) config.php 中的頁面右上方的額外連結
{$WARN_BEKILL} - (提示文字) 此篇文章的附加檔案即將被刪除
{$WARN_ENDREPLY} - (提示文字) 此討論串禁止回應
{$WARN_HIDEPOST} - (提示文字) 此討論串有幾篇回應已隱藏
{$WARN_OLD} - (提示文字) 此篇文章過舊即將被刪除

區塊引用

!!! 警告 !!!
區塊自身引用會導致無限迴圈!
語法:
<!--&(區塊名稱)/-->

範例:
<!--&A-->
<div id="a">
</div>
<!--/&A-->

<!--&B-->
<div id="B">
</div>
<!--&A/-->
<!--/&B-->

類似PHP的 include 敘述,所有變數都能在被引用的區塊中使用。

判斷式

語法:
<!--&IF($(字串名稱),'(有字串則印出文字)','(沒字串則印出文字)')-->
<!--&IF(&(區塊名稱),'(有字串則印出文字)','(沒字串則印出文字)')-->

範例:
<!--&IF($IMG_BAR,'<br />','')-->
<!--&IF(&SHOW_VER,'<!--THEMEVER/-->','')-->

類似一般程式的 if 敘述,但只是單純的字串檢查。先判斷第一個參數的是否有值,有的話輸出第二個參數的值,沒有就輸出第三個參數的值。

*注意:判斷 $(字串名稱) 時,'0'(字串'0')和0(數值0)均算有值,''(空字串)、false(布林值"偽")、null均為無值。
而判斷 &(區塊名稱) 時 '0'(字串'0')、0(數值0)、''(空字串)、false(布林值"偽",找不到區塊時會回傳false)均為無值。(null並不會出現)

判斷引用

!!! 警告 !!!
區塊自身引用會導致無限迴圈!
範例:
<!--&IF($IMG_BAR,'<!--&A/-->','')-->

迴圈

語法:
<!--&FOREACH($(陣列名稱),'(區塊名稱)')-->

範例:
<!--&FOREACH($POST,'THREAD')-->

類似PHP的 foreach 敘述,而第一個參數必須為陣列。

除非特別註明,本頁內容採用以下授權方式: Creative Commons Attribution-Noncommercial-Share Alike 2.5 License.