time() - $ttl) { header('Content-Type: application/rss+xml; charset=utf-8'); readfile($cacheFile); exit; } // Cache miss or stale — fetch and rewrite in one DOMDocument pass $xml = fetchFeed($channel, $limit, false); if ($xml === null) { // Serve stale cache if available if (file_exists($cacheFile)) { header('Content-Type: application/rss+xml; charset=utf-8'); readfile($cacheFile); exit; } // No cache at all — return empty feed so bot doesn't fall back to tg.i-c-a.su header('Content-Type: application/rss+xml; charset=utf-8'); echo '' . htmlspecialchars($channel) . ''; exit; } // Save pre-rewritten XML to cache file_put_contents($cacheFile, $xml); header('Content-Type: application/rss+xml; charset=utf-8'); echo $xml; function fetchFeed(string $channel, int $limit, bool $skipRewrite): ?string { $url = TG_BASE . '/rss/' . $channel . '?limit=' . $limit; $ctx = stream_context_create(['http' => ['timeout' => 30, 'user_agent' => UA, 'follow_location' => true]]); $raw = @file_get_contents($url, false, $ctx); if (!$raw) return null; if ($skipRewrite) { return $raw; } // Parse DOM once, rewrite enclosures, return pre-rewritten XML $doc = new DOMDocument(); @$doc->loadXML($raw); if (!$doc->documentElement) return null; foreach ($doc->getElementsByTagName('enclosure') as $enc) { $encUrl = $enc->getAttribute('url'); if (strpos($encUrl, TG_BASE) !== false) { $enc->setAttribute('url', PROXY_BASE . '/media.php?url=' . urlencode($encUrl)); } } return $doc->saveXML(); }