$segments */ public static function key(string $group, array $segments = []): string { $parts = array_filter( array_map(static fn (string|int $segment): string => (string) $segment, $segments), static fn (string $segment): bool => $segment !== '', ); return implode('.', array_merge(['web', 'g'.self::generation(), $group], $parts)); } /** * Aktuelle Generation der Seiten-Caches. */ public static function generation(): int { return self::$generation ??= (int) Cache::get(self::GenerationKey, 0); } /** * Verwirft die prozessinterne Merkung der Generation, sodass der nächste * Zugriff wieder den Store fragt. Für langlebige Prozesse (Queue-Worker) * und für Tests, die den Cache-Store zurücksetzen. */ public static function resetMemo(): void { self::$generation = null; } /** * Verwirft sämtliche `web.`-Seiten-Caches, indem die Generation erhöht * wird. Die alten Einträge laufen über ihre TTL aus und werden nie wieder * gelesen. */ public static function flush(): void { $current = self::generation(); $next = (int) Cache::increment(self::GenerationKey); // Der Zähler MUSS echt weiterlaufen. Wurde er im Store verworfen // (Eviction, `cache:clear`), liefert increment() wieder 1 und die alte // Generation bliebe gültig — dann würde ein Flush stillschweigend // wirkungslos und entfernte Inhalte blieben in den Listen stehen. if ($next <= $current) { $next = $current + 1; Cache::forever(self::GenerationKey, $next); } self::$generation = $next; } /** * @template TValue * * @param Closure(): TValue $callback * @return TValue */ public static function remember(string $key, Closure $callback): mixed { return Cache::flexible($key, [self::TtlSeconds, self::StaleSeconds], $callback); } }