107 lines
3.2 KiB
PHP
107 lines
3.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\Sitemaps;
|
|
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
final class PublishedSitemapResolver
|
|
{
|
|
public function __construct(private readonly SitemapReleaseManager $releases)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @return array{content: string, release_id: string, document_name: string}|null
|
|
*/
|
|
public function resolveIndex(): ?array
|
|
{
|
|
return $this->resolveDocumentName(SitemapCacheService::INDEX_DOCUMENT);
|
|
}
|
|
|
|
/**
|
|
* @return array{content: string, release_id: string, document_name: string}|null
|
|
*/
|
|
public function resolveNamed(string $requestedName): ?array
|
|
{
|
|
$releaseId = Cache::remember(
|
|
'sitemaps:active-release-id',
|
|
60,
|
|
fn (): ?string => $this->releases->activeReleaseId(),
|
|
);
|
|
|
|
if (! is_string($releaseId) || $releaseId === '') {
|
|
return null;
|
|
}
|
|
|
|
$manifest = Cache::remember(
|
|
'sitemaps:manifest:' . $releaseId,
|
|
3600,
|
|
fn (): ?array => $this->releases->readManifest($releaseId),
|
|
);
|
|
|
|
if (! is_array($manifest)) {
|
|
return null;
|
|
}
|
|
|
|
$documentName = $this->canonicalDocumentName($requestedName, $manifest);
|
|
|
|
return $documentName !== null ? $this->resolveDocumentName($documentName) : null;
|
|
}
|
|
|
|
private function resolveDocumentName(string $documentName): ?array
|
|
{
|
|
$releaseId = Cache::remember(
|
|
'sitemaps:active-release-id',
|
|
60,
|
|
fn (): ?string => $this->releases->activeReleaseId(),
|
|
);
|
|
|
|
if (! is_string($releaseId) || $releaseId === '') {
|
|
return null;
|
|
}
|
|
|
|
$ttl = max((int) config('sitemaps.cache_ttl_seconds', 900), 3600);
|
|
$cacheKey = 'sitemaps:doc:' . $releaseId . ':' . $documentName;
|
|
|
|
$content = Cache::get($cacheKey);
|
|
|
|
if (! is_string($content) || $content === '') {
|
|
$content = $this->releases->getDocument($releaseId, $documentName);
|
|
if (is_string($content) && $content !== '') {
|
|
Cache::put($cacheKey, $content, $ttl);
|
|
}
|
|
}
|
|
|
|
return is_string($content) && $content !== ''
|
|
? ['content' => $content, 'release_id' => $releaseId, 'document_name' => $documentName]
|
|
: null;
|
|
}
|
|
|
|
private function canonicalDocumentName(string $requestedName, array $manifest): ?string
|
|
{
|
|
$documents = (array) ($manifest['documents'] ?? []);
|
|
|
|
if (isset($documents[$requestedName])) {
|
|
return $requestedName;
|
|
}
|
|
|
|
foreach ((array) ($manifest['families'] ?? []) as $familyName => $family) {
|
|
$entryName = (string) ($family['entry_name'] ?? '');
|
|
if ($requestedName === $familyName && $entryName !== '') {
|
|
return $entryName;
|
|
}
|
|
|
|
if (preg_match('/^' . preg_quote((string) $familyName, '/') . '-([0-9]+)$/', $requestedName, $matches)) {
|
|
$number = (int) $matches[1];
|
|
$candidate = sprintf('%s-%04d', $familyName, $number);
|
|
if (in_array($candidate, (array) ($family['shards'] ?? []), true)) {
|
|
return $candidate;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
} |