store->cached($target); if ($cached !== null && $cached['until'] > $now) { return $cached['body'] !== null && SecurityTxtFile::plausible($cached['body']) ? $cached['body'] : null; } try { $response = $this->transport->request('GET', $target, ['Accept' => 'text/plain']); } catch (TransportException) { $response = null; } $body = $response !== null ? SecurityTxtFile::fromResponse($response) : null; if ($body !== null) { $this->store->saveCached($target, $body, $now + self::CACHE_SECONDS); // nur schreiben, was sich geändert hat – die Ablage ist dauerhaft und soll nicht stündlich neu entstehen if ($this->store->lastGood($target) !== $body) { $this->store->saveLastGood($target, $body); } return $body; } if ($response !== null && in_array($response->status, [404, 410], true)) { // DomainWarn sagt ausdrücklich „keine Datei“: Die Auslieferung ist abgeschaltet – dann auch keine alte Fassung $this->store->saveLastGood($target, null); $this->store->saveCached($target, null, $now + self::RETRY_SECONDS); return null; } // nicht erreichbar, überlastet (429, 5xx) oder eine Antwort, die keine security.txt ist: Rückfall $fallback = $this->fallback($target, $now); $expires = $fallback !== null ? (int) SecurityTxtFile::expires($fallback) : 0; $this->store->saveCached($target, $fallback, $fallback !== null ? min($now + self::RETRY_SECONDS, $expires) : $now + self::RETRY_SECONDS); return $fallback; } /** Den Zwischenspeicher leeren; die letzte gute Fassung bleibt. */ public function forget(): void { $this->store->forgetCached(); } /** Die zuletzt gute Fassung, wenn sie noch nicht abgelaufen ist. */ private function fallback(string $target, int $now): ?string { $body = $this->store->lastGood($target); if ($body === null || ! SecurityTxtFile::plausible($body)) { return null; } $expires = SecurityTxtFile::expires($body); return $expires !== null && $expires > $now ? $body : null; } }