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

PHP 產生 Sitemap.xml 完整類別:快取、增量更新與標準格式

     編輯:PHP 綜合知識

完整 Sitemap 產生器類別

<?php
class SitemapGenerator {
    private $filePath;
    private $baseUrl;
    private $cacheHours = 24;
    private $maxUrls = 50000;

    public function __construct(string $baseUrl, string $storagePath, int $cacheHours = 24) {
        $this->baseUrl   = rtrim($baseUrl, '/');
        $this->filePath  = rtrim($storagePath, '/') . '/sitemap.xml';
        $this->cacheHours = $cacheHours;
    }

    public function generate(): string {
        if (!$this->isExpired()) {
            return file_get_contents($this->filePath);
        }

        $urls = $this->collectUrls();
        $xml  = $this->buildXml($urls);
        $this->writeFile($xml);
        return $xml;
    }

    private function isExpired(): bool {
        if (!file_exists($this->filePath)) return true;
        return (time() - filemtime($this->filePath)) > ($this->cacheHours * 3600);
    }

    private function collectUrls(): array {
        $urls = [];
        $now  = date('Y-m-d');

        // 首頁
        $urls[] = [
            'loc'        => $this->baseUrl . '/',
            'lastmod'    => $now,
            'changefreq' => 'daily',
            'priority'   => '1.0',
        ];

        // 動態內容:文章、分類、商品等(範例使用 PDO)
        $pdo = $this->getPdo();
        $stmt = $pdo->query("
            SELECT 'post' as type, slug, updated_at FROM posts WHERE status='published'
            UNION ALL
            SELECT 'category', slug, updated_at FROM categories
            UNION ALL
            SELECT 'product', sku, updated_at FROM products WHERE status=1
            LIMIT {$this->maxUrls}
        ");

        foreach ($stmt as $row) {
            $config = match($row['type']) {
                'post'     => ['path' => '/post/', 'freq' => 'weekly', 'pri' => '0.7'],
                'category' => ['path' => '/category/', 'freq' => 'weekly', 'pri' => '0.8'],
                'product'  => ['path' => '/product/', 'freq' => 'daily',  'pri' => '0.6'],
                default    => ['path' => '/', 'freq' => 'monthly', 'pri' => '0.5'],
            };
            $urls[] = [
                'loc'        => $this->baseUrl . $config['path'] . $row['slug'],
                'lastmod'    => date('Y-m-d', strtotime($row['updated_at'])),
                'changefreq' => $config['freq'],
                'priority'   => $config['pri'],
            ];
        }
        return $urls;
    }

    private function buildXml(array $urls): string {
        $xml = '' . "\n";
        $xml .= '' . "\n";

        foreach ($urls as $url) {
            $xml .= "  \n";
            $xml .= "    " . htmlspecialchars($url['loc'], ENT_QUOTES, 'UTF-8') . "\n";
            $xml .= "    {$url['lastmod']}\n";
            $xml .= "    {$url['changefreq']}\n";
            $xml .= "    {$url['priority']}\n";
            $xml .= "  \n";
        }
        $xml .= '';
        return $xml;
    }

    private function writeFile(string $xml): void {
        $dir = dirname($this->filePath);
        if (!is_dir($dir)) mkdir($dir, 0755, true);
        file_put_contents($this->filePath, $xml);
        // 可選:設定檔案權限
        chmod($this->filePath, 0644);
    }

    private function getPdo(): PDO {
        // 實際專案請從 DI 容器或設定檔取得
        return new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
    }
}

// 使用
$generator = new SitemapGenerator('https://example.com', __DIR__ . '/public');
echo $generator->generate();

增量更新策略(大型網站)

<?php
// 僅收集最近變更的 URL
private function collectUrls(): array {
    $since = date('Y-m-d H:i:s', time() - $this->cacheHours * 3600);
    $stmt = $pdo->prepare("
        SELECT slug, updated_at FROM posts WHERE status='published' AND updated_at > ?
        UNION ALL SELECT slug, updated_at FROM products WHERE status=1 AND updated_at > ?
    ");
    $stmt->execute([$since, $since]);
    // ... 組合 URL

    // 合併現有 sitemap 中未過期的 URL
    if (file_exists($this->filePath)) {
        $existing = $this->parseExistingSitemap();
        $urls = array_merge($existing, $newUrls);
    }
    return $urls;
}

超過 50,000 URL:自動分割 Sitemap Index

<?php
public function generateIndex(): string {
    $allUrls = $this->collectUrls();
    $chunks  = array_chunk($allUrls, $this->maxUrls);
    $indexXml = '' . "\n";
    $indexXml .= '' . "\n";

    foreach ($chunks as $i => $chunk) {
        $filename = "sitemap-" . ($i + 1) . ".xml";
        $this->writeFile($this->buildXml($chunk), $filename);
        $indexXml .= "  \n";
        $indexXml .= "    " . $this->baseUrl . "/$filename\n";
        $indexXml .= "    " . date('Y-m-d') . "\n";
        $indexXml .= "  \n";
    }
    $indexXml .= '';
    file_put_contents(dirname($this->filePath) . '/sitemap.xml', $indexXml);
    return $indexXml;
}

部署建議

  • 設定 Cron Job 每小時/每天執行產生
  • Nginx 直接服務靜態 sitemap.xml,不經 PHP
  • robots.txt 加入 Sitemap: https://example.com/sitemap.xml
  • Google Search Console 定期提交/驗證
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved