靜態化是提升 PHP 應用承載能力的經典策略:將動態頁面預渲染為靜態 HTML,Web 伺服器直接回傳靜態檔案,完全繞過 PHP 解析。本文解析一個輕量的 Shtml 類,實現模板變數替換、目錄自動建立、檔案寫入全流程,適合中小型專案快速接入靜態化。
<FIELD_變數名> 佔位符 → 實際資料<?php
class Shtml
{
public $template = ''; // 模板內容
public $dataSource = []; // 替換資料陣列
public $dir = ''; // 輸出目錄
public $fileName = ''; // 輸出檔案名
public $mode = 'wb'; // 寫入模式
private $handle = false; // 檔案控制代碼
public function __construct(string $fileName = '')
{
$this->fileName = $fileName;
}
// 綁定替換資料
public function bindData(array $data): void
{
$this->dataSource = $data;
}
// 設定輸出目錄
public function setDir(string $dir): void
{
$this->dir = $dir;
}
// 設定檔案名
public function setFileName(string $fileName): string
{
return $this->fileName = $fileName;
}
// 開啟檔案
public function open()
{
$path = $this->resolvePath($this->fileName);
$this->handle = @fopen($path, $this->mode);
return $this->handle;
}
// 寫入內容
public function write(string $content): int|false
{
return $this->handle ? fwrite($this->handle, $content) : false;
}
// 關閉檔案
public function close(): bool
{
return $this->handle ? fclose($this->handle) : false;
}
// 遞迴建立多層目錄
public function mkDir(string $pathname): void
{
$pathname = str_replace('\\', '/', $pathname);
$parts = explode('/', $pathname);
$current = ($parts[0] === '') ? $_SERVER['DOCUMENT_ROOT'] : $_SERVER['DOCUMENT_ROOT'] . dirname($_SERVER['PHP_SELF']);
foreach ($parts as $part) {
if ($part === '') continue;
$current .= '/' . $part;
if (!is_dir($current)) {
@mkdir($current, 0755, true);
}
}
}
// 執行靜態化:替換 → 建立目錄 → 寫入
public function create(): void
{
$content = $this->template;
foreach ($this->dataSource as $key => $val) {
$content = str_replace("", $val, $content);
}
$this->mkDir(dirname($this->fileName));
$this->open();
$this->write($content);
$this->close();
}
private function resolvePath(string $path): string
{
if (str_starts_with($path, '/')) {
return $_SERVER['DOCUMENT_ROOT'] . $path;
}
return $path;
}
}
<?php
function createShtml(): void
{
ob_start('callbackCreateShtml');
}
function callbackCreateShtml(string $buffer): string
{
$page = (int)($_REQUEST['page'] ?? 0);
$shtml = new Shtml();
$base = $_SERVER['DOCUMENT_ROOT'] . dirname($_SERVER['PHP_SELF']);
$script = basename($_SERVER['PHP_SELF'], '.php');
$file = $page > 0 ? "{$script}_{$page}.html" : "{$script}.html";
$shtml->setFileName($base . '/' . $file);
$shtml->template = $buffer;
$shtml->create();
return $buffer; // 仍正常輸出給瀏覽器
}
article.tpl<?php
require 'Shtml.php';
// 1. 讀取模板
$tpl = file_get_contents('article.tpl');
// 2. 準備資料
$data = [
'TITLE' => 'PHP 靜態化實戰',
'AUTHOR' => '開發者',
'DATE' => date('Y-m-d H:i:s'),
'CONTENT' => '這裡是文章內容...'
];
// 3. 執行靜態化
$shtml = new Shtml('/var/www/html/articles/2024/01/15/article-001.html');
$shtml->template = $tpl;
$shtml->bindData($data);
$shtml->create();
echo "靜態頁已生成!";
| 痛點 | 優化方案 |
|---|---|
| 大流量併發寫入同一檔案 | 檔案鎖(flock)/ 暫存檔 + rename 原子替換 |
| 模板語法單一(無邏輯/迴圈) | 引入 Twig/Blade 或自製簡易模板引擎 |
| 靜態檔案更新策略 | 資料變更觸發重新生成 / 定時任務全量重建 / CDN 快取清除 API |
| 分頁/列表頁靜態化 | 生成 index.html、page_2.html... 或使用 Nginx fastcgi_cache 替代 |
| 多伺服器同步 | 共享儲存(NFS/GlusterFS)或部屬時同步推送 |
Nginx fastcgi_cache / Varnish / Cloudflare Page Rules — 無需修改代碼,運維層面解決靜態化靜態化不是銀彈,但對「讀多寫少」場景極具性價比。理解原理、評估成本、選擇合適層次介入,才是架構師的功課。