核心技術棧
- PHP:動態內容生成
- Apache mod_rewrite:URL 重寫與靜態檔存在性判斷
- Output Buffering (ob_*):捕獲 PHP 輸出寫入靜態檔
- file_put_contents:高效寫入檔案
流程架構
- 用戶請求
/html/news_123.html
- Apache 檢查檔案是否存在(
RewriteCond %{REQUEST_FILENAME} !-s)
- 不存在 → 重寫至
getnews.php?id=123 生成靜態檔
- 存在 → 直接服務靜態檔(最高效能)
Apache 重寫規則 .htaccess
RewriteEngine On
RewriteRule ^index.html$ /news.php [L]
RewriteCond %{REQUEST_FILENAME} !-s
RewriteRule ^html/news_([0-9]+).html$ getnews.php?id=$1 [L]
RewriteCond %{REQUEST_FILENAME} !-s:請求檔案不存在或大小為 0 時觸發重寫
- 避免 PHP
file_exists() 造成的無限重導向迴圈
列表頁 news.php
<?php
header("Content-Type: text/html; charset=gbk");
mysql_connect("localhost", "root", "");
mysql_query('SET NAMES gbk');
mysql_select_db("test");
$sql = "SELECT `id`, `title` FROM `arc` ORDER BY `id` DESC";
$rs = mysql_query($sql);
while ($row = mysql_fetch_array($rs)) {
echo "<a href='/Test/html/news_$row[id].html'>$row[title]</a><br>";
}
?>
生成入口 getnews.php
<?php
$id = $_GET['id'];
$root = $_SERVER['DOCUMENT_ROOT'];
$filename = "news_$id.html";
$file = "$root/Test/html/$filename";
ob_start();
include("$root/Test/newsDetail.php");
file_put_contents($file, ob_get_contents());
ob_end_flush();
?>
內容模板 newsDetail.php
<?php
header("Content-Type: text/html; charset=gbk");
if (isset($_GET['id'])) {
$id = $_GET['id'];
} else {
header("Location: https://127.0.0.1/lean/Test/html/news_failed.html");
exit();
}
mysql_connect("localhost", "root", "");
mysql_query('SET NAMES gbk');
mysql_select_db("test");
$sql = "SELECT `news` FROM `arc` WHERE `id`=$id";
$rs = mysql_query($sql);
while ($row = mysql_fetch_array($rs)) {
echo $row['news'];
}
?>
關鍵優化與避坑
| 問題 | 解決方案 |
| 無限重導向 | 使用 RewriteCond 判斷檔案存在性,而非 PHP file_exists() |
| 生成效能 | ob_* + file_put_contents 比 fopen/fwrite/include 快 20 倍 |
| 併發生成衝突 | 加入檔案鎖或檢查檔案存在後再生成 |
| 內容更新同步 | 後台編輯時刪除對應靜態檔,或觸發重新生成 |
| URL 結構調整 | 修改 RewriteRule 正則與生成邏輯即可,無需動業務代碼 |
進階擴充建議
- 引入模板引擎(Smarty/Twig)分離邏輯與視圖
- 支援分頁靜態化(
news_123_2.html)
- CDN 整合:生成至 CDN 同步目錄或推送 API
- 異步生成:訊息佇列處理生成任務,提升回應速度
- 快取失效策略:基於標籤/依賴關係的智能清除