Files
SkinbaseNova/app/Services/Sitemaps/SitemapShardService.php

149 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\Sitemaps;
final class SitemapShardService
{
public function rootEntryName(SitemapBuilder $builder): string
{
$shardCount = $this->shardCount($builder);
if ($builder instanceof ShardableSitemapBuilder && ($shardCount > 1 || $this->forceFamilyIndexes())) {
return $this->familyIndexName($builder->name());
}
return $builder->name();
}
public function shardCount(SitemapBuilder $builder): int
{
if (! $builder instanceof ShardableSitemapBuilder || ! $this->enabledFor($builder->name())) {
return 1;
}
$totalItems = $builder->totalItems();
if ($totalItems <= 0) {
return 0;
}
return max(1, (int) ceil($totalItems / max(1, $builder->shardSize())));
}
/**
* @return list<string>
*/
public function indexNamesForBuilder(SitemapBuilder $builder): array
{
$shardCount = $this->shardCount($builder);
if ($builder instanceof ShardableSitemapBuilder && $shardCount > 1) {
return array_map(fn (int $shard): string => $this->canonicalShardName($builder->name(), $shard), range(1, $shardCount));
}
return [$builder->name()];
}
/**
* @return list<string>
*/
public function canonicalDocumentNamesForBuilder(SitemapBuilder $builder): array
{
$names = $this->indexNamesForBuilder($builder);
if ($builder instanceof ShardableSitemapBuilder && ($this->shardCount($builder) > 1 || $this->forceFamilyIndexes())) {
array_unshift($names, $this->familyIndexName($builder->name()));
}
return array_values(array_unique($names));
}
public function familyIndexName(string $baseName): string
{
return $baseName . '-index';
}
public function canonicalShardName(string $baseName, int $shard): string
{
return sprintf('%s-%s', $baseName, str_pad((string) $shard, $this->padLength(), '0', STR_PAD_LEFT));
}
public function resolve(SitemapRegistry $registry, string $name): ?SitemapTarget
{
$builder = $registry->get($name);
if ($builder !== null) {
$shardCount = $this->shardCount($builder);
if ($builder instanceof ShardableSitemapBuilder && ($shardCount > 1 || $this->forceFamilyIndexes())) {
return new SitemapTarget($name, $this->familyIndexName($builder->name()), $builder->name(), SitemapTarget::TYPE_INDEX, $builder, null, $shardCount);
}
return new SitemapTarget($name, $builder->name(), $builder->name(), $this->targetType($builder), $builder);
}
if (preg_match('/^(.+)-index$/', $name, $indexMatches)) {
$baseName = (string) $indexMatches[1];
$builder = $registry->get($baseName);
if (! $builder instanceof ShardableSitemapBuilder) {
return null;
}
$shardCount = $this->shardCount($builder);
if ($shardCount < 1) {
return null;
}
return new SitemapTarget($name, $this->familyIndexName($baseName), $baseName, SitemapTarget::TYPE_INDEX, $builder, null, $shardCount);
}
if (! preg_match('/^(.+)-([0-9]{1,})$/', $name, $matches)) {
return null;
}
$baseName = (string) $matches[1];
$shardNumber = (int) $matches[2];
$builder = $registry->get($baseName);
if (! $builder instanceof ShardableSitemapBuilder) {
return null;
}
$shardCount = $this->shardCount($builder);
if ($shardCount < 2 || $shardNumber > $shardCount) {
return null;
}
return new SitemapTarget($name, $this->canonicalShardName($baseName, $shardNumber), $baseName, SitemapTarget::TYPE_URLSET, $builder, $shardNumber, $shardCount);
}
private function forceFamilyIndexes(): bool
{
return (bool) config('sitemaps.shards.force_family_indexes', false);
}
private function padLength(): int
{
return max(1, (int) config('sitemaps.shards.zero_pad_length', 4));
}
private function targetType(SitemapBuilder $builder): string
{
return $builder->name() === (string) config('sitemaps.news.google_variant_name', 'news-google')
? SitemapTarget::TYPE_GOOGLE_NEWS
: SitemapTarget::TYPE_URLSET;
}
private function enabledFor(string $family): bool
{
if (! (bool) config('sitemaps.shards.enabled', true)) {
return false;
}
return (int) data_get(config('sitemaps.shards', []), $family . '.size', 0) > 0;
}
}