149 lines
4.6 KiB
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;
|
|
}
|
|
} |