View Source
The actual code behind this page. No minification. No transpilation. What you see is what runs.
|
| 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'; |
| } |
dispelled