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

PHP 解析與生成 RSS 2.0 完整教程

     編輯:PHP 相關知識

RSS(Really Simple Syndication)是內容分發的標準格式,廣泛用於博客、新聞站點、播客等場景。PHP 內置的 XMLReader、SimpleXML、DOMDocument 以及 PEAR 的 XML_RSS 都能處理 RSS。本文涵蓋解析外部 RSS、生成自定義 RSS 兩大核心需求,提供現代最佳實踐代碼。

一、解析外部 RSS Feed

方案 1:SimpleXML(推薦,簡潔易用)

<?php
function parseRss(string $url, int $limit = 20): array {
    // 設置超時,防止掛起
    $context = stream_context_create([
        'http' => ['timeout' => 10, 'user_agent' => 'PHP RSS Parser']
    ]);
    
    $xml = simplexml_load_file($url, 'SimpleXMLElement', LIBXML_NOCDATA, $context);
    if (!$xml) return [];
    
    $items = [];
    foreach ($xml->channel->item as $item) {
        $items[] = [
            'title' => (string)$item->title,
            'link' => (string)$item->link,
            'description' => (string)$item->description,
            'pubDate' => isset($item->pubDate) ? (string)$item->pubDate : '',
            'guid' => isset($item->guid) ? (string)$item->guid : '',
            'author' => isset($item->author) ? (string)$item->author : '',
            'category' => isset($item->category) ? (string)$item->category : '',
            // 媒體附件(播客、圖片)
            'enclosure' => isset($item->enclosure) ? [
                'url' => (string)$item->enclosure['url'],
                'type' => (string)$item->enclosure['type'],
                'length' => (string)$item->enclosure['length']
            ] : null
        ];
        if (count($items) >= $limit) break;
    }
    return $items;
}

// 使用示例
$articles = parseRss('https://www.php.net/feed.atom'); // PHP 官網也支持 Atom
foreach ($articles as $art) {
    echo "{$art['pubDate']} - {$art['title']}
"; }

方案 2:XMLReader(大文件、低內存)

<?php
function parseRssLarge(string $url): Generator {
    $reader = new XMLReader();
    $reader->open($url);
    
    $inItem = false;
    $current = [];
    
    while ($reader->read()) {
        if ($reader->nodeType === XMLReader::ELEMENT) {
            if ($reader->localName === 'item') {
                $inItem = true;
                $current = [];
            } elseif ($inItem && in_array($reader->localName, ['title', 'link', 'description', 'pubDate', 'guid'])) {
                $reader->read(); // 移動到文本節點
                $current[$reader->localName] = $reader->value;
            }
        } elseif ($reader->nodeType === XMLReader::END_ELEMENT && $reader->localName === 'item') {
            $inItem = false;
            yield $current;
        }
    }
    $reader->close();
}

// 逐條處理,內存常數級
foreach (parseRssLarge('https://example.com/large-feed.xml') as $item) {
    // 處理單條,如入庫
    saveToDatabase($item);
}

二、生成標準 RSS 2.0 Feed

使用 DOMDocument 構建結構化 XML,確保輸出合規、編碼正確、可擴展。

<?php
class RssGenerator {
    private $dom;
    private $channel;
    
    public function __construct(array $config) {
        $this->dom = new DOMDocument('1.0', 'UTF-8');
        $this->dom->formatOutput = true; // 可讀性縮進
        
        $rss = $this->dom->createElement('rss');
        $rss->setAttribute('version', '2.0');
        $rss->setAttribute('xmlns:atom', 'https://www.w3.org/2005/Atom');
        $rss->setAttribute('xmlns:content', 'https://purl.org/rss/1.0/modules/content/');
        $this->dom->appendChild($rss);
        
        $channel = $this->dom->createElement('channel');
        $rss->appendChild($channel);
        $this->channel = $channel;
        
        // 必填字段
        $this->addElement('title', $config['title']);
        $this->addElement('link', $config['link']);
        $this->addElement('description', $config['description']);
        $this->addElement('language', $config['language'] ?? 'zh-CN');
        $this->addElement('pubDate', date('r'));
        $this->addElement('lastBuildDate', date('r'));
        $this->addElement('generator', 'Custom PHP RSS Generator');
        
        // Atom 自引用鏈接(SEO 友好)
        $atomLink = $this->dom->createElement('atom:link');
        $atomLink->setAttribute('href', $config['self_link'] ?? $config['link']);
        $atomLink->setAttribute('rel', 'self');
        $atomLink->setAttribute('type', 'application/rss+xml');
        $channel->appendChild($atomLink);
    }
    
    public function addItem(array $item): self {
        $elem = $this->dom->createElement('item');
        
        $this->addChild($elem, 'title', $item['title']);
        $this->addChild($elem, 'link', $item['link']);
        $this->addChild($elem, 'guid', $item['guid'] ?? $item['link'], ['isPermaLink' => 'false']);
        $this->addChild($elem, 'description', $item['description'], [], true); // CDATA
        $this->addChild($elem, 'pubDate', $item['pubDate'] ?? date('r'));
        
        if (!empty($item['author'])) {
            $this->addChild($elem, 'author', $item['author']);
        }
        if (!empty($item['category'])) {
            foreach ((array)$item['category'] as $cat) {
                $this->addChild($elem, 'category', $cat);
            }
        }
        if (!empty($item['enclosure'])) {
            $enc = $this->dom->createElement('enclosure');
            $enc->setAttribute('url', $item['enclosure']['url']);
            $enc->setAttribute('type', $item['enclosure']['type']);
            $enc->setAttribute('length', $item['enclosure']['length']);
            $elem->appendChild($enc);
        }
        // 支持全文內容模塊
        if (!empty($item['content'])) {
            $content = $this->dom->createElement('content:encoded');
            $content->appendChild($this->dom->createCDATASection($item['content']));
            $elem->appendChild($content);
        }
        
        $this->channel->appendChild($elem);
        return $this;
    }
    
    public function output(): void {
        header('Content-Type: application/rss+xml; charset=UTF-8');
        header('Cache-Control: public, max-age=3600');
        echo $this->dom->saveXML();
    }
    
    private function addElement(string $name, string $value, array $attrs = []): void {
        $el = $this->dom->createElement($name, htmlspecialchars($value, ENT_XML1, 'UTF-8'));
        foreach ($attrs as $k => $v) $el->setAttribute($k, $v);
        $this->channel->appendChild($el);
    }
    
    private function addChild(DOMElement $parent, string $name, string $value, array $attrs = [], bool $cdata = false): void {
        $el = $this->dom->createElement($name);
        foreach ($attrs as $k => $v) $el->setAttribute($k, $v);
        if ($cdata) {
            $el->appendChild($this->dom->createCDATASection($value));
        } else {
            $el->appendChild($this->dom->createTextNode($value));
        }
        $parent->appendChild($el);
    }
}

// 使用示例
$rss = new RssGenerator([
    'title' => '技術博客',
    'link' => 'https://blog.example.com',
    'description' => '分享 PHP、Laravel、架構設計等技術文章',
    'language' => 'zh-CN',
    'self_link' => 'https://blog.example.com/rss.xml'
]);

// 從數據庫獲取文章
$articles = $pdo->query('SELECT id, title, slug, excerpt, content, created_at FROM posts WHERE status=1 ORDER BY created_at DESC LIMIT 20')->fetchAll();
foreach ($articles as $art) {
    $rss->addItem([
        'title' => $art['title'],
        'link' => "https://blog.example.com/post/{$art['slug']}",
        'guid' => "post-{$art['id']}",
        'description' => $art['excerpt'],
        'content' => $art['content'], // 全文
        'pubDate' => date('r', strtotime($art['created_at'])),
        'category' => ['PHP', '後端開發'],
        'author' => 'author@example.com (作者名)'
    ]);
}

$rss->output();

三、關鍵最佳實踐

  1. 編碼統一 UTF-8:數據庫、PHP 文件、XML 聲明、HTTP Header 全鏈路 UTF-8。
  2. 日期格式 RFC 2822:使用 date('r') 或 DateTime::RFC2822,如 Mon, 15 Jan 2024 10:30:00 +0800。
  3. CDATA 保護內容:description、content:encoded 使用 CDATA 避免 HTML 實體破壞 XML 結構。
  4. GUID 唯一標識:推薦 isPermaLink="false" + 業務唯一 ID(如 post-123)。
  5. 緩存與條件請求:生成端設置 ETag/Last-Modified,解析端發送 If-None-Match/If-Modified-Since 減少流量。
  6. 限制條目數:單個 Feed 建議 20-50 條,過大影響解析器性能。

四、常見問題排查

現象可能原因解決
輸出亂碼文件編碼非 UTF-8 或未發送正確 Header文件存為 UTF-8 無 BOM,header('Content-Type: application/rss+xml; charset=UTF-8')
XML 解析錯誤內容包含非法字符(如控制字符)preg_replace('/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/', '', $str) 清洗
圖片不顯示description 為純文本或相對路徑使用 content:encoded 放全文 HTML,圖片用絕對 URL
訂閱器報錯缺少必要字段或命名空間參考 W3C Feed Validator 驗證

RSS 雖然「老派」,但在內容分發、播客、Newsletter、跨站聚合等場景仍不可替代。掌握 PHP 處理 RSS 的正確姿勢,能為你的產品增加一條穩定的內容觸達通道。

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