View Source

The actual code behind this page. No minification. No transpilation. What you see is what runs.

↓ Download PHP edition
rss.php 119 lines
<?php
function fetchFeed(string $url, string $sourceId, string $sourceLabel): array
{
$cacheFile = CACHE_DIR . '/feed_' . $sourceId . '.json';
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < CACHE_TTL_FEED) {
$cached = json_decode(file_get_contents($cacheFile), true);
if ($cached) return $cached;
}
$ctx = stream_context_create([
'http' => [
'method' => 'GET',
'header' => "User-Agent: Mozilla/5.0 (compatible; dispelled.ca/2.0)\r\n",
'timeout' => 8,
],
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
],
]);
$raw = @file_get_contents($url, false, $ctx);
if (!$raw) return [];
libxml_use_internal_errors(true);
$xml = @simplexml_load_string($raw);
libxml_clear_errors();
if (!$xml) return [];
// RSS 1.0 (RDF) uses <rdf:RDF> root with top-level <item> in the RSS 1.0 namespace.
// RSS 2.0 uses <rss><channel><item>.
$rss1ns = 'http://purl.org/rss/1.0/';
$isRss1 = $xml->getName() === 'RDF';
$itemList = $isRss1
? ($xml->children($rss1ns)->item ?? [])
: ($xml->channel->item ?? []);
if (empty($itemList)) return [];
$items = [];
foreach ($itemList as $item) {
if ($isRss1) {
$rssEl = $item->children($rss1ns);
$dcEl = $item->children('http://purl.org/dc/elements/1.1/');
$title = (string)($rssEl->title ?? '');
$link = (string)($rssEl->link ?? '');
$desc = (string)($rssEl->description ?? '');
$pubDate = (string)($dcEl->date ?? '');
} else {
$contentNs = $item->children('content', true);
$title = (string)($item->title ?? '');
$link = (string)($item->link ?? '');
$desc = isset($contentNs->encoded)
? (string)$contentNs->encoded
: (string)($item->description ?? '');
$pubDate = (string)($item->pubDate ?? '');
}
$desc = strip_tags($desc);
$desc = html_entity_decode($desc, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$desc = preg_replace('/\s+/', ' ', $desc);
$desc = trim(mb_substr($desc, 0, 400));
if (strtolower($desc) === 'comments') { $desc = ''; }
if ($title === '' && $link === '') continue;
$items[] = [
'id' => $sourceId . '-' . md5($link),
'title' => html_entity_decode($title, ENT_QUOTES | ENT_HTML5, 'UTF-8'),
'url' => $link,
'description' => $desc !== '' ? $desc : null,
'source' => $sourceId,
'sourceLabel' => $sourceLabel,
'publishedAt' => $pubDate,
];
}
if (!is_dir(CACHE_DIR)) mkdir(CACHE_DIR, 0755, true);
file_put_contents($cacheFile, json_encode($items));
return $items;
}
function getAllFeedItems(): array
{
$sources = [
['id' => 'slashdot', 'label' => 'Slashdot', 'url' => 'http://rss.slashdot.org/Slashdot/slashdotMain'],
['id' => 'ars', 'label' => 'Ars Technica', 'url' => 'https://feeds.arstechnica.com/arstechnica/index'],
['id' => 'hn', 'label' => 'Hacker News', 'url' => 'https://news.ycombinator.com/rss'],
['id' => 'register', 'label' => 'The Register', 'url' => 'https://www.theregister.com/headlines.rss'],
['id' => 'phoronix', 'label' => 'Phoronix', 'url' => 'https://www.phoronix.com/rss.php'],
['id' => 'lwn', 'label' => 'LWN.net', 'url' => 'https://lwn.net/headlines/rss'],
['id' => 'eff', 'label' => 'EFF', 'url' => 'https://www.eff.org/rss/updates.xml'],
['id' => 'krebs', 'label' => 'Krebs on Security','url' => 'https://krebsonsecurity.com/feed/'],
];
$all = [];
foreach ($sources as $s) {
$all = array_merge($all, fetchFeed($s['url'], $s['id'], $s['label']));
}
usort($all, fn($a, $b) => strtotime($b['publishedAt']) - strtotime($a['publishedAt']));
return $all;
}
function timeAgo(string $dateStr): string
{
$t = @strtotime($dateStr);
if (!$t) return '';
$diff = time() - $t;
if ($diff < 60) return 'just now';
if ($diff < 3600) return (int)($diff / 60) . ' min ago';
if ($diff < 86400) return (int)($diff / 3600) . ' hr ago';
return (int)($diff / 86400) . ' days ago';
}