XOOPS 2.7 全面升級到 Smarty 4 後,舊佈景或舊模組最容易炸掉的地方,就是樣板裡的 `<{php}>...<{/php}>`。
原因很單純:Smarty 4 已經不支援在樣板中直接執行 PHP。也就是說,舊寫法如果還把 PHP 邏輯塞在 `.tpl` 裡,升級後就會出現 unknown tag、白畫面或 SmartyCompilerException。
目前比較穩的解法,不是去 hack Smarty,也不是讓 Smarty 重新開放任意 PHP,而是把原本散落在樣板中的 PHP 程式碼收回 PHP 檔案,整理成 function,再用 `$tpl->registerPlugin()` 註冊成 Smarty 標籤,最後在樣板中用新標籤取代原本的 `<{php}>`。
簡單說:
舊寫法:
.tpl 直接寫 <{php}> PHP CODE <{/php}>
新寫法:
PHP 檔建立 function
→ registerPlugin 註冊成 Smarty function
→ .tpl 改用 <{php_channel}>
這樣效果接近原本 `<{php}>`,但執行入口變乾淨,也比較符合 Smarty 4 的規則。
修改方法如下說明:
一、建立相容函式檔
建議先建立一個 PHP 檔,專門收納原本樣板裡的流浪 PHP,例如:
modules/模組ID/function/tpl_smarty_compat.php
示例:
<?php
//Smarty 4 樣板流浪 PHP 收容區
function register_smarty_compat($tpl=""){
if(empty($tpl) || !is_object($tpl)) return;
if(!method_exists($tpl,'registerPlugin')) return;
static $registered=array();
$tplid=function_exists('spl_object_id') ? spl_object_id($tpl) : spl_object_hash($tpl);
if(!empty($registered[$tplid])) return;
$tpl->registerPlugin('function','php_channel','smarty_php_channel');
//統一放置註冊區,只需要把流量php封裝好放這裡即可
//$tpl->registerPlugin('function','php_channel_2','smarty_php_channel_2');
//$tpl->registerPlugin('function','php_channel_3','smarty_php_channel_3');
$registered[$tplid]=true;
}
//原本 <{php}> 裡面的程式碼,改放到這裡
function smarty_php_channel($params,$template){
$box="123"; //示例:這裡放原本 <{php}> CODE 產生的結果
return $box;
}
重點:
- `registerPlugin('function','php_channel','smarty_php_channel')` 代表註冊一個 Smarty 標籤 `<{php_channel}>`
- `smarty_php_channel()` 的 `return` 內容,就是樣板會輸出的內容
- `static $registered` 是避免同一個 request 重複註冊,否則清快取或重複 include 時可能噴 already registered
二、在正確時機載入註冊檔
這段不要放在 `.tpl` 裡面,而是要放在會比樣板編譯更早執行的 PHP 入口,例如:
- `modules/模組ID/preloads/core.php`
- 佈景的 `theme_autorun.php`
- 模組自己的前置載入檔
示例:
<?php
$compat_file=XOOPS_ROOT_PATH.'/modules/模組ID/function/tpl_smarty_compat.php';
if(file_exists($compat_file)) include_once $compat_file;
if(function_exists('register_smarty_compat')){
global $xoopsTpl,$xoTheme;
$tplList=array();
if(!empty($xoopsTpl) && is_object($xoopsTpl)) $tplList[]=$xoopsTpl;
if(!empty($xoTheme) && !empty($xoTheme->template) && is_object($xoTheme->template)) $tplList[]=$xoTheme->template;
foreach($tplList as $tpl){
register_smarty_compat($tpl);
}
}
這裡同時註冊 `$xoopsTpl` 與 `$xoTheme->template`,是因為 XOOPS / theme / module 在不同階段可能使用不同 Smarty 物件。若只註冊其中一個,有時會發生某些樣板抓得到標籤、某些樣板卻噴 unknown tag。
三、修改樣板呼叫方式
原本樣板:
<{php}>
//原本的 PHP CODE
echo "123";
<{/php}>
改成:
<{php_channel}>
若需要傳參數,也可以這樣:
<{php_channel id=$uid mode="view"}>
PHP function 內可用 `$params` 接:
function smarty_php_channel($params,$template){
$uid=!empty($params['id']) ? (int)$params['id'] : 0;
$mode=!empty($params['mode']) ? $params['mode'] : '';
return $uid.'-'.$mode;
}
四、實務注意事項
1. 不建議 hack Smarty vendor,升級後會很難維護。
2. 不建議恢復任意 PHP 執行,安全風險太高。
3. 每一段流浪 PHP 最好整理成一個白名單 function。
4. function 裡面若有重邏輯,建議先用路徑、參數或頁面條件判斷,避免每頁都跑。
Smarty 4 不支援 `<{php}>` 不是壞事,它只是逼我們把樣板裡的 PHP 收回到 PHP 檔案管理。
以 XOOPS 2.7 來說,最穩的升級方式是:
<{php}> 流浪 PHP
→ 搬到 tpl_smarty_compat.php
→ registerPlugin 註冊
→ 樣板改用 <{xxx}> 標籤
這樣不需要大改模組,也不用重構整套佈景,就能讓舊 XOOPS 2.5.11 的樣板邏輯逐步相容 XOOPS 2.7 / Smarty 4。
以上工作心得撰寫,有需要的朋友參考看看!
工作心得撰寫:徐嘉裕 Neil hsu
留言
張貼留言