= $ttl) { $data = @file_get_contents($url); if ($data !== false) file_put_contents($cache, $data); } if (!file_exists($cache)) return []; return _ical_upcoming(file_get_contents($cache), $n); } function _ical_upcoming(string $ical, int $n): array { $events = []; $now = time(); preg_match_all('/BEGIN:VEVENT(.+?)END:VEVENT/s', $ical, $blocks); foreach ($blocks[1] as $b) { $e = []; if (preg_match('/^SUMMARY[^:]*:(.+)$/m', $b, $m)) $e['title'] = trim($m[1]); if (preg_match('/^LOCATION[^:]*:(.+)$/m', $b, $m)) $e['location'] = trim($m[1]); if (preg_match('/^DTSTART[^:]*:(\d+)/m', $b, $m)) { $r = $m[1]; $e['start'] = strlen($r) === 8 ? mktime(0, 0, 0, substr($r, 4, 2), substr($r, 6, 2), substr($r, 0, 4)) : strtotime($r); } if (isset($e['start'], $e['title']) && $e['start'] >= $now) $events[] = $e; } usort($events, fn($a, $b) => $a['start'] - $b['start']); return array_slice($events, 0, $n); }