最簡單的模板機制
<?php
// 控制器
$data = [
'title' => '網站標題',
'items' => ['首頁', '關於', '聯絡'],
];
include 'template.php';
<!-- template.php -->
<html>
<head><title><?= $title ?></title></head>
<body>
<ul>
<?php foreach ($items as $item): ?>
<li><?= $item ?></li>
<?php endforeach; ?>
</ul>
</body>
</html>
封裝為函式:解決作用域污染
<?php
function render($templateFile, array $data = []) {
// 1. 將陣列鍵值轉為區域變數
extract($data, EXTR_SKIP); // EXTR_SKIP 避免覆蓋既有變數
// 2. 開啟輸出緩衝,捕捉 include 輸出
ob_start();
include $templateFile;
return ob_get_clean();
}
// 使用
echo render('template.php', [
'title' => '動態標題',
'items' => ['A', 'B', 'C'],
]);
進階:模板類別化管理
<?php
class TemplateEngine {
private $vars = [];
private $templateDir;
public function __construct(string $templateDir) {
$this->templateDir = rtrim($templateDir, '/');
}
public function assign(string $key, $value): self {
$this->vars[$key] = $value;
return $this;
}
public function render(string $template, bool $return = false) {
$file = $this->templateDir . '/' . $template . '.php';
if (!is_file($file)) {
throw new Exception("Template not found: $file");
}
extract($this->vars, EXTR_SKIP);
ob_start();
include $file;
$output = ob_get_clean();
if ($return) return $output;
echo $output;
}
}
// 使用
$tpl = new TemplateEngine(__DIR__ . '/views');
$tpl->assign('siteName', 'My Site')
->assign('user', ['name' => 'John', 'role' => 'admin'])
->render('layout');
核心原理解析
| 步驟 | 函式/機制 | 說明 |
| 資料傳遞 | extract() | 將關聯陣列轉為符號表變數,EXTR_SKIP 防止覆蓋 |
| 輸出捕捉 | ob_start()/ob_get_clean() | 將 include 輸出導向緩衝區,支援巢狀模板 |
| 作用域隔離 | 函式/方法區域作用域 | 模板內變數不會污染全域,include 繼承呼叫者作用域 |
安全性考量
- 禁止在模板執行任意 PHP:僅允許
echo、foreach、if 等輸出邏輯
- 輸出轉義:預設所有變數輸出經
htmlspecialchars(),需原始 HTML 時顯式標記
- 模板檔案路徑驗證:防止目錄遍歷攻擊
輸出轉義助手
<?php
function e($value) {
return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
}
// 模板中
<h1><?= e($title) ?></h1>
<div class="raw"><?= $trustedHtml ?></div> <!-- 需開發者確信安全 -->