cahuk / sitemap-generator
Flexible PHP sitemap generator
Installs: 2
Dependents: 0
Suggesters: 0
Security: 0
Stars: 2
Watchers: 2
Forks: 0
Open Issues: 0
pkg:composer/cahuk/sitemap-generator
Requires
- php: >=8.3.0
- ext-dom: *
Requires (Dev)
- phpunit/phpunit: ^12.3
README
A flexible, high-performance PHP library for generating XML sitemaps of any scale.
Supports multiple URL providers, automatic partitioning, indexing, configurable priorities, frequencies, timestamps, and flexible storage options.
This package follows a clean DDD-inspired structure with separation between Domain, Application, and Infrastructure layers.
๐ Features
- Generate XML sitemaps with zero external dependencies
- Multiple sitemap sections (pages, blog, categories, etc.)
- Automatic sitemap partitioning (e.g., split every 50,000 URLs or at your choice)
- Fully customizable URL providers
- Built-in XML renderer
- Local filesystem writer (can be extended to S3, FTP, DB, etc.)
- Strong DTOs, enums, and typed entries
- Simple integration into any PHP project (Symfony, Laravel, custom framework)
๐ Installation
Install via Composer:
composer require cahuk/sitemap-generator
#๐งฉ Basic Example Below is a real working example based directly on this package:
declare(strict_types = 1); use Cahuk\SitemapGenerator\Application\Service\SitemapGenerator; use Cahuk\SitemapGenerator\Domain\Model\Enum\ChangeFrequencyEnum; use Cahuk\SitemapGenerator\Application\Service\SitemapPartitioner; use Cahuk\SitemapGenerator\Infrastructure\Renderer\XmlSitemapRenderer; use Cahuk\SitemapGenerator\Application\Dto\SitemapGeneratorSettingDto; use Cahuk\SitemapGenerator\Infrastructure\Repository\LocalFilesystemWriter; use Cahuk\SitemapGenerator\Infrastructure\EntryProvider\ArrayUrlEntryProvider; require_once './vendor/autoload.php'; // 1. Page URLs provider $pagesEntriesProvider = new ArrayUrlEntryProvider( name : 'sitemap-pages', urlEntryData : [ 'https://example.com/' => ['lastModified' => '2025-01-01'], 'https://example.com/page/our-team' => ['lastModified' => '2025-01-01'], 'https://example.com/page/contact-ua' => ['lastModified' => '2025-01-01'], 'https://example.com/page/terms' => ['lastModified' => '2025-01-01'], 'https://example.com/page/reviews' => ['lastModified' => '2025-01-01'], 'https://example.com/page/reviews/john-doe' => ['lastModified' => '2025-01-01'], ], defPriority : 0.8, defLastModified: new DateTimeImmutable('-10 days'), defChangeFreq : ChangeFrequencyEnum::Monthly, ); // 2. Blog URLs provider $blogEntriesProvider = new ArrayUrlEntryProvider( name : 'sitemap-blog', urlEntryData: [ 'https://example.com/blog/hot-topic' => [ 'lastModified' => '2025-01-01', 'priority' => 0.7, 'changeFreq' => 'daily', ], 'https://example.com/blog/case-study' => [ 'lastModified' => '2025-01-01', 'priority' => 0.5, 'changeFreq' => 'monthly', ], 'https://example.com/blog/how-to-start' => [ 'lastModified' => '2025-01-01', 'priority' => 0.5, 'changeFreq' => 'yearly', ], ] ); // 3. Maximum URLs per sitemap file $maxUrlsPerSitemap = 5000; // 4. Renderer + filesystem adapter $sitemapRender = new XmlSitemapRenderer(); // 5. Generator $sitemapGenerator = new SitemapGenerator( repository : new LocalFilesystemWriter( basePath : "./", renderer : $sitemapRender, indexRenderer: $sitemapRender, ), generatorSetting: new SitemapGeneratorSettingDto( host: 'https://freedemo.games/', ), partitioner : new SitemapPartitioner( $maxUrlsPerSitemap, ...[$pagesEntriesProvider, $blogEntriesProvider], ), ); // 6. Generate $result = $sitemapGenerator->generateSitemap(); echo "Generated:" . PHP_EOL; $result = [ 'generated_at' => $result->generatedAt()->format(DATE_W3C), 'duration' => $result->getDuration(), 'urls_count' => $result->getUrlCount(), 'path' => $result->getPath(), 'file_names' => implode(', ', $result->getFileNames()), 'memory_usage_bytes' => $result->getMemoryUsedBytes(), 'memory_peak_usage_bytes' => $result->getMemoryPeakUsageBytes(), ]; foreach ($result as $field => $value) { echo "$field: $value " . PHP_EOL; }
#๐ Output The generator produces:
sitemap.xml sitemap-pages.xml sitemap-blog.xml
With automatic when needed. Example of generated index:
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <sitemap> <loc>https://freedemo.games/sitemap-pages.xml</loc> <lastmod>2025-01-01</lastmod> </sitemap> <sitemap> <loc>https://freedemo.games/sitemap-blog.xml</loc> <lastmod>2025-01-01</lastmod> </sitemap> </sitemapindex>
#๐งฑ URL Entry Providers This package ships out of the box with a ready-to-use provider: โ ArrayUrlEntryProvider (built-in) A simple provider that accepts an associative array where:
- keys = URL strings
- values = metadata (lastModified, priority, changeFreq, ...)
$pagesEntriesProvider = new ArrayUrlEntryProvider( name : 'sitemap-pages', urlEntryData : [ 'https://example.com/' => [ 'lastModified' => '2025-01-01', ], 'https://example.com/page/our-team' => [ 'lastModified' => '2025-01-01', ], ], defPriority : 0.8, defLastModified: new DateTimeImmutable(), defChangeFreq : ChangeFrequencyEnum::Monthly, );
#๐งฉ Creating Custom Providers If the built-in ArrayUrlEntryProvider is not enough, you can easily create your own custom provider by implementing:
Cahuk\SitemapGenerator\Domain\Model\EntryProviderInterface
This allows you to fetch URLs from:
- a database
- an API
- CMS
- search index
- filesystem
- any dynamic source Example skeleton:
<?php declare(strict_types = 1); namespace Cahuk\SitemapGenerator\Infrastructure\EntryProvider; use Override; use PDO; use Exception; use DateTimeImmutable; use Cahuk\SitemapGenerator\Domain\Model\UrlEntry; use Cahuk\SitemapGenerator\Domain\Model\ValueObject\Url; use Cahuk\SitemapGenerator\Domain\Model\ValueObject\Priority; use Cahuk\SitemapGenerator\Domain\Model\Enum\ChangeFrequencyEnum; use Cahuk\SitemapGenerator\Domain\Model\ValueObject\ChangeFrequency; use Cahuk\SitemapGenerator\Domain\EntryProvider\UrlEntryProviderInterface; /** * Class MyCustomProvider * * Example of a custom URL provider powered by database entries. */ final class MyCustomProvider implements UrlEntryProviderInterface { private string $name; private PDO $pdo; private ?float $defPriority; private ?DateTimeImmutable $defLastModified; private ?ChangeFrequencyEnum $defChangeFreq; /** * @param string $name * @param PDO $pdo * @param float|null $defPriority * @param DateTimeImmutable|null $defLastModified * @param ChangeFrequencyEnum|null $defChangeFreq */ public function __construct( string $name, PDO $pdo, ?float $defPriority = null, ?DateTimeImmutable $defLastModified = null, ?ChangeFrequencyEnum $defChangeFreq = null, ) { $this->name = $name; $this->pdo = $pdo; $this->defPriority = $defPriority; $this->defLastModified = $defLastModified; $this->defChangeFreq = $defChangeFreq; } #[Override] public function getName(): string { return $this->name; } /** * Returns iterable list of UrlEntry objects from the database. * * Your database table must contain at least: * - url (string) * - updated_at (optional, datetime) * - priority (optional float) * - change_freq (optional string matching ChangeFrequencyEnum) * * @throws Exception */ #[Override] public function getUrlEntry(): iterable { $stmt = $this->pdo->query(" SELECT url, updated_at, priority, change_freq FROM sitemap_urls "); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { // URL value object $url = new Url($row['url']); // Priority $priority = isset($row['priority']) ? new Priority((float)$row['priority']) : ($this->defPriority ? new Priority($this->defPriority) : null); // Last modified $lastModified = !empty($row['updated_at']) ? new DateTimeImmutable($row['updated_at']) : $this->defLastModified; // Change frequency $changeFreq = !empty($row['change_freq']) ? new ChangeFrequency(ChangeFrequencyEnum::from($row['change_freq'])) : ($this->defChangeFreq ? new ChangeFrequency($this->defChangeFreq) : null); yield new UrlEntry( url : $url, lastModified: $lastModified, priority : $priority, changeFreq : $changeFreq, ); } } }
#๐งช Performance
- The generator tracks:
- Execution time
- URL count
- Memory usage
- Peak memory usage
generated_at: 2025-01-01T10:00:00+00:00 duration: 0.183s urls_count: 42 path: ./sitemap.xml file_names: sitemap.xml, sitemap-pages.xml, sitemap-blog.xml memory_usage_bytes: 24576 memory_peak_usage_bytes: 49152