RSS 仍是內容發佈、部落格聚合、播客發布的標準格式。本文提供一組完整、符合 RSS 2.0 規範的 PHP 類別(`RSS` + `RSSItem`),支援頻道層級與項目層級的自訂標籤、發布時間管理、Enclosure 附件(播客必備)、CDATA 內容保護,並附上資料庫整合範例。
class RSS {
public $title = '';
public $link = '';
public $description = '';
public $language = 'zh-TW';
public $pubDate = '';
protected $items = [];
protected $tags = [];
public function addItem(RSSItem $item): self { $this->items[] = $item; return $this; }
public function addTag(string $tag, string $value): self { $this->tags[$tag] = $value; return $this; }
public function setPubDate(string|int $when): self {
$ts = is_int($when) ? $when : strtotime($when);
$this->pubDate = $ts ? date('D, d M Y H:i:s', $ts) . ' GMT' : date('D, d M Y H:i:s') . ' GMT';
return $this;
}
public function out(): string {
$out = $this->header();
$out .= "<channel>\n";
$out .= "<title>" . $this->escape($this->title) . "</title>\n";
$out .= "<link>" . $this->escape($this->link) . "</link>\n";
$out .= "<description>" . $this->escape($this->description) . "</description>\n";
$out .= "<language>" . $this->escape($this->language) . "</language>\n";
$out .= "<pubDate>" . $this->pubDate . "</pubDate>\n";
foreach ($this->tags as $k => $v) $out .= "<$k>" . $this->escape($v) . "</$k>\n";
foreach ($this->items as $item) $out .= $item->out();
$out .= "</channel>\n";
$out .= $this->footer();
return str_replace('&', '&', $out); // 最後統一轉義 &
}
public function serve(string $contentType = 'application/xml; charset=utf-8'): void {
header("Content-Type: $contentType");
echo $this->out();
}
protected function header(): string {
return '<?xml version="1.0" encoding="utf-8"?>' . "\n"
. '<rss version="2.0" xmlns:dc="https://purl.org/dc/elements/1.1/">' . "\n";
}
protected function footer(): string { return '</rss>'; }
protected function escape(string $s): string { return htmlspecialchars($s, ENT_XML1 | ENT_QUOTES, 'UTF-8'); }
}
class RSSItem {
public $title = '';
public $link = '';
public $description = '';
public $pubDate = '';
public $guid = '';
protected $tags = [];
protected $enclosure = '';
public function addTag(string $tag, string $value): self { $this->tags[$tag] = $value; return $this; }
public function setPubDate(string|int $when): self {
$ts = is_int($when) ? $when : strtotime($when);
$this->pubDate = $ts ? date('D, d M Y H:i:s', $ts) . ' GMT' : date('D, d M Y H:i:s') . ' GMT';
return $this;
}
public function enclosure(string $url, string $mimeType, int $length): self {
$this->enclosure = "<enclosure url='" . $this->escape($url) . "' length='$length' type='" . $this->escape($mimeType) . "' />";
return $this;
}
public function out(): string {
$out = "<item>\n";
$out .= "<title>" . $this->escape($this->title) . "</title>\n";
$out .= "<link>" . $this->escape($this->link) . "</link>\n";
// 使用 CDATA 保護 HTML 內容
$out .= "<description><![CDATA[" . $this->description . "]]></description>\n";
$out .= "<pubDate>" . $this->pubDate . "</pubDate>\n";
if ($this->enclosure) $out .= $this->enclosure . "\n";
$out .= "<guid isPermaLink='" . (filter_var($this->guid ?: $this->link, FILTER_VALIDATE_URL) ? 'true' : 'false') . "'>"
. $this->escape($this->guid ?: $this->link) . "</guid>\n";
foreach ($this->tags as $k => $v) $out .= "<$k>" . $this->escape($v) . "</$k>\n";
$out .= "</item>\n";
return $out;
}
protected function escape(string $s): string { return htmlspecialchars($s, ENT_XML1 | ENT_QUOTES, 'UTF-8'); }
}
<?php
require_once 'RSS.php';
$pdo = new PDO('mysql:host=localhost;dbname=blog;charset=utf8mb4', 'user', 'pass');
$stmt = $pdo->query('SELECT id, title, slug, excerpt, content, created_at FROM articles WHERE status="published" ORDER BY created_at DESC LIMIT 20');
$feed = new RSS();
$feed->title = '我的技術部落格';
$feed->link = 'https://blog.example.com';
$feed->description = '分享 PHP、架構、DevOps 實戰經驗';
$feed->setPubDate(time());
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$item = new RSSItem();
$item->title = $row['title'];
$item->link = 'https://blog.example.com/article/' . $row['slug'];
$item->description = $row['content']; // 完整內容含 HTML
$item->setPubDate($row['created_at']);
$item->guid = $item->link;
// 可選:加入作者、分類等自訂標籤
$item->addTag('dc:creator', 'Author Name');
$item->addTag('category', 'PHP');
$feed->addItem($item);
}
$feed->serve(); // 直接輸出 XML 並設定正確 Content-Type
$item->enclosure(
'https://cdn.example.com/episodes/ep001.mp3',
'audio/mpeg',
15728640 // 檔案大小 bytes
);
// 產生:
<?xml version="1.0" encoding="utf-8"?> <rss version="2.0" xmlns:dc="https://purl.org/dc/elements/1.1/"> <channel> <title>我的技術部落格</title> <link>https://blog.example.com</link> <description>分享 PHP、架構、DevOps 實戰經驗</description> <language>zh-TW</language> <pubDate>Mon, 15 Jan 2024 08:30:00 GMT</pubDate> <item> <title>PHP 8.2 新特性深度解析</title> <link>https://blog.example.com/article/php-82-features</link> <description><![CDATA[<p>PHP 8.2 帶來了 readonly class、DNF 型別...</p>]]></description> <pubDate>Mon, 15 Jan 2024 08:00:00 GMT</pubDate> <guid isPermaLink='true'>https://blog.example.com/article/php-82-features</guid> <dc:creator>Author Name</dc:creator> <category>PHP</category> </item> ... </channel> </rss>
將此類別納入專案共用庫,即可快速為任何內容型網站新增標準 RSS Feed,支援讀者訂閱、聚合平台收錄、播客平台發布等多元場景。