高流量動態頁面頻繁查詢資料庫會造成效能瓶頸。將熱門頁面生成靜態 HTML 檔案,讓 Web Server 直接服務靜態檔,可大幅降低 PHP 與 MySQL 負載。本文提供一個通用的靜態化方法並討論進階策略。
/**
* 生成靜態 HTML 檔案
* @param string $root 儲存目錄(需可寫入)
* @param string $rule 檔名規則(不含副檔名)
* @param string $url 動態頁面完整 URL
* @return bool|int 寫入位元組數或 false
*/
public function setHtmlCache($root, $rule, $url) {
$filePath = $root . $rule . '.html';
// 若已存在則刪除重建,確保內容最新
if (file_exists($filePath)) {
@unlink($filePath);
}
// 抓取動態頁面內容並寫入靜態檔
return file_put_contents($filePath, file_get_contents($url));
}
// 生成首頁靜態檔
$htmlCache->setHtmlCache('/var/www/html/cache/', 'index', 'https://example.com/');
// 生成文章頁靜態檔(依 ID 命名)
$htmlCache->setHtmlCache('/var/www/html/cache/article/', 'article_12345', 'https://example.com/article/12345');
// 檔案修改時間超過 1 小時視為過期
if (file_exists($filePath) && (time() - filemtime($filePath) < 3600)) {
return readfile($filePath); // 直接輸出靜態檔
}
// 否則重新生成...
$lockFile = $filePath . '.lock';
$fp = fopen($lockFile, 'w');
if (flock($fp, LOCK_EX | LOCK_NB)) {
// 獲得鎖,生成靜態檔
file_put_contents($filePath, file_get_contents($url));
flock($fp, LOCK_UN);
} else {
// 未獲得鎖,直接讀取現有舊檔(若存在)
if (file_exists($filePath)) readfile($filePath);
}
fclose($fp);
# nginx.conf
location / {
try_files /cache/$uri.html /cache/$uri/index.html @php;
}
location @php {
fastcgi_pass unix:/var/run/php-fpm.sock;
include fastcgi_params;
}
請求優先尋找 /cache/ 目錄下對應靜態檔,找不到才轉交 PHP-FPM 處理動態生成。
index.en.html、index.zh.htmlindex.mobile.html、index.desktop.htmlindex.loggedin.html、index.guest.html