首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 批量生成靜態頁面:模板替換與效能優化實戰

     編輯:PHP 相關知識

需求場景

將動態用戶資料批量生成靜態 HTML 頁面(如 user/user-123.html),降低資料庫壓力、提升前端載入速度、利於 SEO。

原始代碼問題分析

<?php
// 原始碼主要問題:
// 1. fopen('moban.html', 'r') 放在循環外,但 fclose($fp) 在循環內 → 第二次迴代報錯
// 2. $str = fread(...) 只讀一次,但循環中反覆 str_replace 會累積替換(第二輪在已替換的字串上再替換)
// 3. 逐用戶查詢資料庫(N+1 問題),極慢
// 4. 無錯誤處理、無併發控制、無進度顯示
?>

優化版完整實現

<?php
// config.php
return [
    'db' => [
        'dsn' => 'mysql:host=127.0.0.1;dbname=cms;charset=utf8mb4',
        'user' => 'root',
        'pass' => 'password',
        'options' => [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
    ],
    'template' => __DIR__ . '/templates/user_detail.tpl',
    'outputDir' => __DIR__ . '/public/user/',
    'batchSize' => 100, // 分批處理,避免記憶體溢出
];

// generate.php
require 'vendor/autoload.php';
$config = require 'config.php';

$pdo = new PDO($config['db']['dsn'], $config['db']['user'], $config['db']['pass'], $config['db']['options']);

// 1. 一次性讀取模板
$template = file_get_contents($config['template']);
if ($template === false) {
    die("模板文件讀取失敗: {$config['template']}");
}

// 2. 確保輸出目錄存在
if (!is_dir($config['outputDir'])) {
    mkdir($config['outputDir'], 0755, true);
}

// 3. 分批查詢用戶 ID(游標/生成器模式更佳,這裡用 LIMIT/OFFSET 示意)
$page = 1;
$pageSize = $config['batchSize'];
$totalGenerated = 0;
$startTime = microtime(true);

while (true) {
    $stmt = $pdo->prepare('SELECT uid, username, email, reg_time, profile FROM users LIMIT ? OFFSET ?');
    $stmt->execute([$pageSize, ($page - 1) * $pageSize]);
    $users = $stmt->fetchAll(PDO::FETCH_ASSOC);

    if (empty($users)) break; // 無更多資料

    foreach ($users as $user) {
        // 4. 每次基於原始模板替換(避免累積替換)
        $html = $template;
        $html = str_replace(
            ['{username}', '{email}', '{reg_time}', '{profile}'],
            [
                htmlspecialchars($user['username']),
                htmlspecialchars($user['email']),
                date('Y-m-d H:i:s', strtotime($user['reg_time'])),
                nl2br(htmlspecialchars($user['profile']))
            ],
            $html
        );

        // 5. 寫入靜態文件(原子寫入:寫臨時文件 -> rename)
        $fileName = "user-{$user['uid']}.html";
        $filePath = $config['outputDir'] . $fileName;
        $tmpPath = $filePath . '.tmp';

        if (file_put_contents($tmpPath, $html) === false) {
            error_log("寫入失敗: $filePath");
            continue;
        }
        if (!rename($tmpPath, $filePath)) {
            error_log("重命名失敗: $filePath");
            @unlink($tmpPath);
            continue;
        }

        $totalGenerated++;
    }

    $page++;
    // 可選:釋放大變數
    unset($users);
    gc_collect_cycles();
}

$elapsed = microtime(true) - $startTime;
echo "生成完成!共 $totalGenerated 頁,耗時 " . number_format($elapsed, 3) . " 秒\n";
?>

關鍵優化點解析

優化項原始問題解決方案
模板讀取循環內重複讀取/關閉循環外 file_get_contents 一次讀取
替換邏輯累積替換導致錯誤每次 $html = $template 重置,使用陣列 str_replace 一次性替換
資料庫查詢N+1 查詢分批 SELECT ... LIMIT/OFFSET 或游標 PDO::FETCH_ORI_NEXT
檔案寫入直接寫入可能產生殘檔臨時檔 .tmp + rename() 原子操作
XSS 防護無轉義htmlspecialchars() + nl2br() 安全輸出
記憶體控制大量用戶撐爆記憶體分批處理 + unset() + gc_collect_cycles()

進階擴充:併發生成(多進程)

<?php
// 使用 Symfony Process 或 pcntl_fork 併發
// 將用戶 ID 陣列切分為 N 份,啟動 N 個 Worker 進程並行生成
// 主進程監控進度、收集錯誤、合併日誌
// 注意:資料庫連線數、磁碟 I/O 瓶頸、檔案系統 inode 限制
?>

部署建議

  • Nginx 配置:try_files /user/user-$uid.html @php; 靜態檔存在直接服務,不存在回落 PHP 動態生成(或 404)。
  • 定時重建:Cron 每日凌晨重建,或資料變更時推送到佇列增量更新。
  • CDN 快取:靜態 HTML 上傳至對象儲存(OSS/S3)+ CDN,源站完全卸載。
  • 監控:生成耗時、成功率、磁碟佔用、最舊頁面年齡。

結語

批量靜態化核心在於 「模板一次讀、資料分批取、每輪重置替換、原子寫入檔」。結合分批處理、佇列異步、CDN 分發,即可構建高效能、可擴展的靜態站點架構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved