Introduction
RSS feeds are not dead. They're essential for content distribution, SEO, and keeping engaged audiences updated. Here's how I built an RSS feed generator for the blog content on ViralVidVault that outputs valid Atom/RSS 2.0 XML.
Why RSS for a Video Platform?
Three reasons:
- Search engines crawl RSS feeds for content discovery
- Feed readers (Feedly, Inoreader) drive consistent traffic
- Podcatchers and aggregators can pull your video blog content automatically
The Generator
<?php
class RssFeedGenerator
{
private string $siteUrl;
private string $siteName;
private string $siteDescription;
public function __construct(
string $siteUrl = 'https://viralvidvault.com',
string $siteName = 'ViralVidVault',
string $siteDescription = 'Your vault of viral videos from around the world',
) {
$this->siteUrl = rtrim($siteUrl, '/');
$this->siteName = $siteName;
$this->siteDescription = $siteDescription;
}
public function generate(array $articles): string
{
$xml = new \DOMDocument('1.0', 'UTF-8');
$xml->formatOutput = true;
$rss = $xml->createElement('rss');
$rss->setAttribute('version', '2.0');
$rss->setAttribute('xmlns:atom', 'http://www.w3.org/2005/Atom');
$rss->setAttribute('xmlns:media', 'http://search.yahoo.com/mrss/');
$xml->appendChild($rss);
$channel = $xml->createElement('channel');
$rss->appendChild($channel);
// Channel metadata
$channel->appendChild($xml->createElement('title', $this->siteName));
$channel->appendChild($xml->createElement('link', $this->siteUrl));
$channel->appendChild($xml->createElement('description', $this->siteDescription));
$channel->appendChild($xml->createElement('language', 'en'));
$channel->appendChild($xml->createElement('lastBuildDate', date(DATE_RSS)));
// Atom self-link
$atomLink = $xml->createElement('atom:link');
$atomLink->setAttribute('href', "{$this->siteUrl}/feed.xml");
$atomLink->setAttribute('rel', 'self');
$atomLink->setAttribute('type', 'application/rss+xml');
$channel->appendChild($atomLink);
// Items
foreach ($articles as $article) {
$item = $this->createItem($xml, $article);
$channel->appendChild($item);
}
return $xml->saveXML();
}
private function createItem(\DOMDocument $xml, array $article): \DOMElement
{
$item = $xml->createElement('item');
$item->appendChild($xml->createElement('title', htmlspecialchars($article['title'])));
$item->appendChild($xml->createElement('link', "{$this->siteUrl}/blog/{$article['slug']}"));
$guid = $xml->createElement('guid', "{$this->siteUrl}/blog/{$article['slug']}");
$guid->setAttribute('isPermaLink', 'true');
$item->appendChild($guid);
$item->appendChild($xml->createElement('pubDate', date(DATE_RSS, strtotime($article['published_at']))));
// Description with CDATA
$desc = $xml->createElement('description');
$desc->appendChild($xml->createCDATASection($article['excerpt'] ?? substr($article['body'], 0, 300) . '...'));
$item->appendChild($desc);
// Categories
foreach ($article['tags'] ?? [] as $tag) {
$item->appendChild($xml->createElement('category', $tag));
}
// Media thumbnail if available
if (!empty($article['thumbnail'])) {
$media = $xml->createElement('media:thumbnail');
$media->setAttribute('url', $article['thumbnail']);
$item->appendChild($media);
}
return $item;
}
}
Serving the Feed
<?php
// Route: /feed.xml
$db = new Database(__DIR__ . '/../data/articles.db');
$articles = $db->getRecentArticles(limit: 20);
$generator = new RssFeedGenerator();
$xml = $generator->generate($articles);
header('Content-Type: application/rss+xml; charset=utf-8');
header('Cache-Control: public, max-age=3600');
echo $xml;
Caching the Feed
RSS feeds don't need to be generated on every request. Cache the output:
$cacheFile = __DIR__ . '/../data/pagecache/feed.xml';
$cacheTtl = 3600; // 1 hour
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $cacheTtl) {
header('Content-Type: application/rss+xml; charset=utf-8');
readfile($cacheFile);
exit;
}
// Generate fresh feed
$xml = $generator->generate($articles);
file_put_contents($cacheFile, $xml, LOCK_EX);
header('Content-Type: application/rss+xml; charset=utf-8');
echo $xml;
Autodiscovery
Add this to your HTML <head> so feed readers can find it:
<link rel="alternate" type="application/rss+xml"
title="ViralVidVault Blog Feed"
href="https://viralvidvault.com/feed.xml">
Validating Your Feed
Use the W3C Feed Validation Service at https://validator.w3.org/feed/ to check your output. Common issues:
- Missing
guidelements - Invalid date formats (must be RFC 2822)
- Unescaped HTML in descriptions (use CDATA)
- Missing
xmlnsdeclarations
The RSS feed at viralvidvault.com passes W3C validation and is indexed by major feed aggregators. It's a small effort that yields steady, recurring traffic.
Part of the "Building ViralVidVault" series.
Top comments (0)