{"id":23434915,"url":"https://github.com/gpslab/sitemap","last_synced_at":"2025-07-27T07:07:50.620Z","repository":{"id":49046373,"uuid":"68381260","full_name":"gpslab/sitemap","owner":"gpslab","description":"sitemap.xml and index for sitemap.xml builder","archived":false,"fork":false,"pushed_at":"2021-06-30T08:14:09.000Z","size":635,"stargazers_count":8,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"2.x","last_synced_at":"2025-06-11T02:49:43.193Z","etag":null,"topics":["buffer","deflate","gzip","php","seo","sitemap","sitemap-builder","sitemap-generator","sitemap-index","sitemap-xml","stream","streaming"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gpslab.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-09-16T13:18:21.000Z","updated_at":"2024-09-14T11:39:08.000Z","dependencies_parsed_at":"2022-09-06T22:13:24.875Z","dependency_job_id":null,"html_url":"https://github.com/gpslab/sitemap","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/gpslab/sitemap","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gpslab%2Fsitemap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gpslab%2Fsitemap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gpslab%2Fsitemap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gpslab%2Fsitemap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gpslab","download_url":"https://codeload.github.com/gpslab/sitemap/tar.gz/refs/heads/2.x","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gpslab%2Fsitemap/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267320256,"owners_count":24068527,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-27T02:00:11.917Z","response_time":82,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["buffer","deflate","gzip","php","seo","sitemap","sitemap-builder","sitemap-generator","sitemap-index","sitemap-xml","stream","streaming"],"created_at":"2024-12-23T12:33:47.053Z","updated_at":"2025-07-27T07:07:50.591Z","avatar_url":"https://github.com/gpslab.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Latest Stable Version](https://img.shields.io/packagist/v/gpslab/sitemap.svg?maxAge=3600\u0026label=stable)](https://packagist.org/packages/gpslab/sitemap)\n[![Build Status](https://img.shields.io/travis/gpslab/sitemap.svg?maxAge=3600)](https://travis-ci.org/gpslab/sitemap)\n[![Coverage Status](https://img.shields.io/coveralls/gpslab/sitemap.svg?maxAge=3600)](https://coveralls.io/github/gpslab/sitemap?branch=master)\n[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/gpslab/sitemap.svg?maxAge=3600)](https://scrutinizer-ci.com/g/gpslab/sitemap/?branch=master)\n[![License](https://img.shields.io/packagist/l/gpslab/sitemap.svg?maxAge=3600)](https://github.com/gpslab/sitemap)\n\nSitemap.xml Generation Framework\n================================\n\nThis is a framework for streaming build Sitemaps.xml and index of Sitemap.xml.\n\nSee [Sitemap.xml protocol](https://www.sitemaps.org/protocol.html) for more details.\n\n## Features\n\n * Streaming build (saves RAM);\n * Parallel multiple streaming;\n * Specify localized URL version;\n * Automatically calculate URL priority;\n * Automatically calculate URL change frequency;\n * Sitemap overflow tracking by total links;\n * Sitemap overflow tracking by used size;\n * [Protocol](https://www.sitemaps.org/protocol.html) compliance tracking;\n * Compression in gzip and deflate;\n * Build a Sitemap for a site section (not only the root `sitemap.xml`);\n * Groups URLs in several Sitemaps;\n * Use URLs building services;\n * Create a Sitemap with several URLs building services;\n * Write a Sitemap to the file;\n * Sends a Sitemap to the output buffer;\n * Write a Sitemap index into the file;\n * Split a Sitemap on overflow;\n * Split a Sitemap on overflow and write a part of Sitemap into the Sitemap.xml index;\n * Write a Sitemap to a temporary folder to save the valid `sitemap.xml` in the destination path during build;\n * Render a Sitemap by [XMLWriter](https://www.php.net/manual/en/book.xmlwriter.php);\n * Render a Sitemap as a plain text without any dependencies;\n * Compressed or formatted XML;\n * XML schema validation.\n\n## Group build\n\nThis is an example of how the `sitemap.xml` can be build by your console command. In this example, all site links are\ndivided into groups and a build service is created for each group. In this example, a sitemap is build from 6675 links,\nbut this approach also facilitates the build of large site maps for 100000 or 500000 links.\n\n![Example build sitemap.xml](build.png)\n\n## Installation\n\nPretty simple with [Composer](https://packagist.org), run:\n\n```sh\ncomposer require gpslab/sitemap\n```\n\n## Simple usage\n\n```php\n// URLs on your site\n$urls = [\n    Url::create(\n        'https://example.com/', // loc\n        new \\DateTimeImmutable('2020-06-15 13:39:46'), // lastmod\n        ChangeFrequency::always(), // changefreq\n        10 // priority\n    ),\n    Url::create(\n        'https://example.com/contacts.html',\n        new \\DateTimeImmutable('2020-05-26 09:28:12'),\n        ChangeFrequency::monthly(),\n        7\n    ),\n    Url::create('https://example.com/about.html'),\n];\n\n// file into which we will write a sitemap\n$filename = __DIR__.'/sitemap.xml';\n\n// configure stream\n$render = new PlainTextSitemapRender();\n$writer = new TempFileWriter();\n$stream = new WritingStream($render, $writer, $filename);\n\n// build sitemap.xml\n$stream-\u003eopen();\nforeach ($urls as $url) {\n    $stream-\u003epush($url);\n}\n$stream-\u003eclose();\n```\n\nResult `sitemap.xml`:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003curlset xmlns=\"https://www.sitemaps.org/schemas/sitemap/0.9\"\u003e\n    \u003curl\u003e\n        \u003cloc\u003ehttps://example.com/\u003c/loc\u003e\n        \u003clastmod\u003e2020-06-15T13:39:46+03:00\u003c/lastmod\u003e\n        \u003cchangefreq\u003ealways\u003c/changefreq\u003e\n        \u003cpriority\u003e1.0\u003c/priority\u003e\n    \u003c/url\u003e\n    \u003curl\u003e\n        \u003cloc\u003ehttps://example.com//contacts.html\u003c/loc\u003e\n        \u003clastmod\u003e2020-05-26T09:28:12+03:00\u003c/lastmod\u003e\n        \u003cchangefreq\u003emonthly\u003c/changefreq\u003e\n        \u003cpriority\u003e0.7\u003c/priority\u003e\n    \u003c/url\u003e\n    \u003curl\u003e\n        \u003cloc\u003ehttps://example.com/about.html\u003c/loc\u003e\n    \u003c/url\u003e\n\u003c/urlset\u003e\n```\n\n## Change frequency\n\nHow frequently the page is likely to change. This value provides general information to search engines and may not\ncorrelate exactly to how often they crawl the page.\n\nYou can define it:\n\n * As string\n \n   ```php\n   $change_frequency = 'daily';\n   ```\n\n * As constant\n \n   ```php\n   $change_frequency = ChangeFrequency::DAILY;\n   ```\n\n * As object\n \n   ```php\n   $change_frequency = ChangeFrequency::daily();\n   ```\n\n## Priority\n\nThe priority of this URL relative to other URLs on your site. Valid values range from `0.0` to `1.0`. This value does not\naffect how your pages are compared to pages on other sites-it only lets the search bots know which pages you deem\nmost important for the search bots.\n\nYou can define it:\n\n * As string\n \n   ```php\n   $priority = '0.5';\n   ```\n\n * As float\n \n   ```php\n   $priority = .5;\n   ```\n\n * As integer\n \n   ```php\n   $priority = 5;\n   ```\n\n * As object\n \n   ```php\n   $priority = Priority::create(5 /* string|float|int */);\n   ```\n\n## Localized versions of page\n\nIf you have multiple versions of a page for different languages or regions, tell search bots about these different\nvariations. Doing so will help search bots point users to the most appropriate version of your page by language or\nregion.\n\n```php\n// URLs on your site\n$urls = [\n    Url::create(\n        'https://example.com/english/page.html',\n        new \\DateTimeImmutable('2020-06-15 13:39:46'),\n        ChangeFrequency::monthly(),\n        7,\n        [\n            'de' =\u003e 'https://example.com/deutsch/page.html',\n            'de-ch' =\u003e 'https://example.com/schweiz-deutsch/page.html',\n            'en' =\u003e 'https://example.com/english/page.html',\n            'fr' =\u003e 'https://example.fr',\n            'x-default' =\u003e 'https://example.com/english/page.html',\n        ]\n    ),\n    Url::create(\n        'https://example.com/deutsch/page.html',\n        new \\DateTimeImmutable('2020-06-15 13:39:46'),\n        ChangeFrequency::monthly(),\n        7,\n        [\n            'de' =\u003e 'https://example.com/deutsch/page.html',\n            'de-ch' =\u003e 'https://example.com/schweiz-deutsch/page.html',\n            'en' =\u003e 'https://example.com/english/page.html',\n            'fr' =\u003e 'https://example.fr',\n            'x-default' =\u003e 'https://example.com/english/page.html',\n        ]\n    ),\n    Url::create(\n        'https://example.com/schweiz-deutsch/page.html',\n        new \\DateTimeImmutable('2020-06-15 13:39:46'),\n        ChangeFrequency::monthly(),\n        7,\n        [\n            'de' =\u003e 'https://example.com/deutsch/page.html',\n            'de-ch' =\u003e 'https://example.com/schweiz-deutsch/page.html',\n            'en' =\u003e 'https://example.com/english/page.html',\n            'fr' =\u003e 'https://example.fr',\n            'x-default' =\u003e 'https://example.com/english/page.html',\n        ]\n    ),\n];\n```\n\nYou can simplify the creation of URLs for localized versions of the same page within the same domain.\n\n```php\n$urls = Url::createLanguageUrls(\n    [\n        'de' =\u003e 'https://example.com/deutsch/page.html',\n        'de-ch' =\u003e 'https://example.com/schweiz-deutsch/page.html',\n        'en' =\u003e 'https://example.com/english/page.html',\n        'x-default' =\u003e 'https://example.com/english/page.html',\n    ],\n    new \\DateTimeImmutable('2020-06-15 13:39:46'),\n    ChangeFrequency::monthly(),\n    7,\n    [\n        'fr' =\u003e 'https://example.fr',\n    ]\n);\n```\n\nResult `sitemap.xml`:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003curlset xmlns=\"https://www.sitemaps.org/schemas/sitemap/0.9\"\u003e\n    \u003curl\u003e\n        \u003cloc\u003ehttps://example.com/deutsch/page.html\u003c/loc\u003e\n        \u003clastmod\u003e2020-06-15T13:39:46+03:00\u003c/lastmod\u003e\n        \u003cchangefreq\u003emonthly\u003c/changefreq\u003e\n        \u003cpriority\u003e0.7\u003c/priority\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"de\" href=\"https://example.com/deutsch/page.html\"/\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"de-ch\" href=\"https://example.com/schweiz-deutsch/page.html\"/\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"en\" href=\"https://example.com/english/page.html\"/\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"x-default\" href=\"https://example.com/english/page.html\"/\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"fr\" href=\"https://example.fr\"/\u003e\n    \u003c/url\u003e\n    \u003curl\u003e\n        \u003cloc\u003ehttps://example.com/schweiz-deutsch/page.html\u003c/loc\u003e\n        \u003clastmod\u003e2020-06-15T13:39:46+03:00\u003c/lastmod\u003e\n        \u003cchangefreq\u003emonthly\u003c/changefreq\u003e\n        \u003cpriority\u003e0.7\u003c/priority\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"de\" href=\"https://example.com/deutsch/page.html\"/\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"de-ch\" href=\"https://example.com/schweiz-deutsch/page.html\"/\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"en\" href=\"https://example.com/english/page.html\"/\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"x-default\" href=\"https://example.com/english/page.html\"/\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"fr\" href=\"https://example.fr\"/\u003e\n    \u003c/url\u003e\n    \u003curl\u003e\n        \u003cloc\u003ehttps://example.com/english/page.html\u003c/loc\u003e\n        \u003clastmod\u003e2020-06-15T13:39:46+03:00\u003c/lastmod\u003e\n        \u003cchangefreq\u003emonthly\u003c/changefreq\u003e\n        \u003cpriority\u003e0.7\u003c/priority\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"de\" href=\"https://example.com/deutsch/page.html\"/\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"de-ch\" href=\"https://example.com/schweiz-deutsch/page.html\"/\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"en\" href=\"https://example.com/english/page.html\"/\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"x-default\" href=\"https://example.com/english/page.html\"/\u003e\n        \u003cxhtml:link rel=\"alternate\" hreflang=\"fr\" href=\"https://example.fr\"/\u003e\n    \u003c/url\u003e\n\u003c/urlset\u003e\n```\n\n## URL builders\n\nYou can create a service that will return a links to pages of your site.\n\n```php\nclass MySiteUrlBuilder implements UrlBuilder\n{\n    public function getIterator(): \\Traversable\n    {\n        // add URLs on your site\n        return new \\ArrayIterator([\n          Url::create(\n              'https://example.com/', // loc\n              new \\DateTimeImmutable('2020-06-15 13:39:46'), // lastmod\n              ChangeFrequency::always(), // changefreq\n              10 // priority\n          ),\n          Url::create(\n              'https://example.com/contacts.html',\n              new \\DateTimeImmutable('2020-05-26 09:28:12'),\n              ChangeFrequency::monthly(),\n              7\n          ),\n          Url::create(\n              'https://example.com/about.html',\n              new \\DateTimeImmutable('2020-05-02 17:12:38'),\n              ChangeFrequency::monthly(),\n              7\n          ),\n       ]);\n    }\n}\n```\n\nIt was a simple build. We add a builder more complicated.\n\n```php\nclass ArticlesUrlBuilder implements UrlBuilder\n{\n    private $pdo;\n\n    public function __construct(\\PDO $pdo)\n    {\n        $this-\u003epdo = $pdo;\n    }\n\n    public function getIterator(): \\Traversable\n    {\n        $section_update_at = null;\n        $sth = $this-\u003epdo-\u003equery('SELECT id, update_at FROM article');\n        $sth-\u003eexecute();\n\n        while ($row = $sth-\u003efetch(PDO::FETCH_ASSOC)) {\n            $update_at = new \\DateTimeImmutable($row['update_at']);\n            $section_update_at = max($section_update_at, $update_at);\n\n            // smart URL automatically fills fields that it can\n            yield Url::createSmart(\n                sprintf('https://example.com/article/%d', $row['id']),\n                $update_at\n            );\n        }\n\n        // link to section\n        if ($section_update_at !== null) {\n            yield Url::createSmart('https://example.com/article/', $section_update_at);\n        } else {\n            yield Url::create(\n                'https://example.com/article/',\n                new \\DateTimeImmutable('-1 day'),\n                ChangeFrequency::daily(),\n                9\n            );\n        }\n    }\n}\n```\n\nWe take one of the exists builders and configure it.\n\n```php\n// collect a collection of builders\n$builders = new MultiUrlBuilder([\n    new MySiteUrlBuilder(),\n    new ArticlesUrlBuilder(/* $pdo */),\n]);\n\n// file into which we will write a sitemap\n$filename = __DIR__.'/sitemap.xml';\n\n// configure stream\n$render = new PlainTextSitemapRender();\n$writer = new TempFileWriter();\n$stream = new WritingStream($render, $writer, $filename);\n\n// build sitemap.xml\n$stream-\u003eopen();\nforeach ($builders as $url) {\n    $stream-\u003epush($url);\n}\n$stream-\u003eclose();\n```\n\n## Sitemap index\n\nYou can create [Sitemap index](https://www.sitemaps.org/protocol.html#index) to group multiple sitemap files. If you\nhave already created portions of the Sitemap, you can simply create the Sitemap index.\n\n```php\n// file into which we will write a sitemap\n$filename = __DIR__.'/sitemap.xml';\n\n// configure stream\n$render = new PlainTextSitemapIndexRender();\n$writer = new TempFileWriter();\n$stream = new WritingIndexStream($render, $writer, $filename);\n\n// build sitemap.xml index\n$stream-\u003eopen();\n$stream-\u003epushSitemap(new Sitemap('https://example.com/sitemap_main.xml', new \\DateTimeImmutable('-1 hour')));\n$stream-\u003epushSitemap(new Sitemap('https://example.com/sitemap_news.xml', new \\DateTimeImmutable('-1 hour')));\n$stream-\u003epushSitemap(new Sitemap('https://example.com/sitemap_articles.xml', new \\DateTimeImmutable('-1 hour')));\n$stream-\u003eclose();\n```\n\nResult `sitemap.xml`:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003csitemapindex xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd\" xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\u003e\n    \u003csitemap\u003e\n        \u003cloc\u003ehttps://example.com/sitemap_main.xml\u003c/loc\u003e\n        \u003clastmod\u003e2020-06-15T13:39:46+03:00\u003c/lastmod\u003e\n    \u003c/sitemap\u003e\n    \u003csitemap\u003e\n        \u003cloc\u003ehttps://example.com/sitemap_news.xml\u003c/loc\u003e\n        \u003clastmod\u003e2020-06-15T13:39:46+03:00\u003c/lastmod\u003e\n    \u003c/sitemap\u003e\n    \u003csitemap\u003e\n        \u003cloc\u003ehttps://example.com/sitemap_articles.xml\u003c/loc\u003e\n        \u003clastmod\u003e2020-06-15T13:39:46+03:00\u003c/lastmod\u003e\n    \u003c/sitemap\u003e\n\u003c/sitemapindex\u003e\n```\n\n## Split URLs and make Sitemap index\n\nYou can simplify splitting the list of URLs to partitions and creating a Sitemap index.\n\nYou can push URLs into the `WritingSplitIndexStream` stream and he will write them to the partition of the Sitemap.\nUpon reaching the partition size limit, the stream closes this partition, adds it to the index and opens the next\npartition. This simplifies the building of a big sitemap and eliminates the need for follow size limits.\n\nYou'll get a Sitemap index `sitemap.xml` and a few partitions `sitemap1.xml`, `sitemap2.xml`, `sitemapN.xml`  from a\nlarge number of URLs.\n\n```php\n// collect a collection of builders\n$builders = new MultiUrlBuilder([\n    new MySiteUrlBuilder(),\n    new ArticlesUrlBuilder(/* $pdo */),\n]);\n\n// file into which we will write a sitemap\n$index_filename = __DIR__.'/sitemap.xml';\n\n$index_render = new PlainTextSitemapIndexRender();\n$index_writer = new TempFileWriter();\n\n// file into which we will write a sitemap part\n// filename should contain a directive like \"%d\"\n$part_filename = __DIR__.'/sitemap%d.xml';\n\n// web path to the sitemap.xml on your site\n$part_web_path = 'https://example.com/sitemap%d.xml';\n\n$part_render = new PlainTextSitemapRender();\n// separate writer for part\n// it's better not to use one writer as a part writer and a index writer\n// this can cause conflicts in the writer\n$part_writer = new TempFileWriter();\n\n// configure stream\n$stream = new WritingSplitIndexStream(\n    $index_render,\n    $part_render,\n    $index_writer,\n    $part_writer,\n    $index_filename,\n    $part_filename,\n    $part_web_path\n);\n\n$stream-\u003eopen();\n\n// build sitemap.xml index file and sitemap1.xml, sitemap2.xml, sitemapN.xml with URLs\nforeach ($builders as $url) {\n    $stream-\u003epush($url);\n}\n\n// you can add a link to a sitemap created earlier\n$stream-\u003epushSitemap(new Sitemap('https://example.com/sitemap_news.xml', new \\DateTimeImmutable('-1 hour')));\n\n$stream-\u003eclose();\n```\n\nAs a result, you will get a file structure like this:\n\n```\nsitemap.xml\nsitemap1.xml\nsitemap2.xml\nsitemap3.xml\nsitemap_news.xml\n```\n\nResult `sitemap.xml`:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003csitemapindex xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd\" xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\u003e\n    \u003csitemap\u003e\n        \u003cloc\u003ehttps://example.com/sitemap1.xml\u003c/loc\u003e\n        \u003clastmod\u003e2020-06-15T13:39:46+03:00\u003c/lastmod\u003e\n    \u003c/sitemap\u003e\n    \u003csitemap\u003e\n        \u003cloc\u003ehttps://example.com/sitemap2.xml\u003c/loc\u003e\n        \u003clastmod\u003e2020-06-15T13:39:46+03:00\u003c/lastmod\u003e\n    \u003c/sitemap\u003e\n    \u003csitemap\u003e\n        \u003cloc\u003ehttps://example.com/sitemap3.xml\u003c/loc\u003e\n        \u003clastmod\u003e2020-06-15T13:39:46+03:00\u003c/lastmod\u003e\n    \u003c/sitemap\u003e\n    \u003csitemap\u003e\n        \u003cloc\u003ehttps://example.com/sitemap_news.xml\u003c/loc\u003e\n        \u003clastmod\u003e2020-06-15T13:39:46+03:00\u003c/lastmod\u003e\n    \u003c/sitemap\u003e\n\u003c/sitemapindex\u003e\n```\n\n## Split URLs in groups\n\nYou may not want to break all URLs to a partitions like with `WritingSplitIndexStream` stream. You might want to make\nseveral partition groups. For example, to create a partition group that contains only URLs to news on your website, a\npartition group for articles, and a group with all other URLs.\n\nThis can help identify problems in a specific URLs group. Also, you can configure your application to reassemble only\nindividual groups if necessary, and not the entire map.\n\n***Note.** The list of partitions is stored in the `WritingSplitStream` stream and a large number of partitions\nwill spend memory.*\n\n```php\n// file into which we will write a sitemap\n$index_filename = __DIR__.'/sitemap.xml';\n\n$index_render = new PlainTextSitemapIndexRender();\n$index_writer = new TempFileWriter();\n\n// separate writer for part\n$part_writer = new TempFileWriter();\n$part_render = new PlainTextSitemapRender();\n\n$index_stream = new WritingIndexStream($index_render, $index_writer, $index_filename);\n\n// create a stream for news\n\n// file into which we will write a sitemap part\n// filename should contain a directive like \"%d\"\n$news_filename = __DIR__.'/sitemap_news%d.xml';\n// web path to sitemap parts on your site\n$news_web_path = 'https://example.com/sitemap_news%d.xml';\n$news_stream = new WritingSplitStream($part_render, $part_writer, $news_filename, $news_web_path);\n\n// similarly create a stream for articles\n$articles_filename = __DIR__.'/sitemap_articles%d.xml';\n$articles_web_path = 'https://example.com/sitemap_articles%d.xml';\n$articles_stream = new WritingSplitStream($part_render, $part_writer, $articles_filename, $articles_web_path);\n\n// similarly create a main stream\n$main_filename = __DIR__.'/sitemap_main%d.xml';\n$main_web_path = 'https://example.com/sitemap_main%d.xml';\n$main_stream = new WritingSplitStream($part_render, $part_writer, $main_filename, $main_web_path);\n\n// build sitemap.xml index\n$index_stream-\u003eopen();\n\n$news_stream-\u003eopen();\n// build parts of a sitemap group\nforeach ($news_urls as $url) {\n    $news_stream-\u003epush($url);\n}\n\n// add all parts to the index\nforeach ($news_stream-\u003egetSitemaps() as $sitemap) {\n    $index_stream-\u003epushSitemap($sitemap);\n}\n\n// close the stream only after adding all parts to the index\n// otherwise the list of parts will be cleared\n$news_stream-\u003eclose();\n\n// similarly for articles stream\n$articles_stream-\u003eopen();\nforeach ($article_urls as $url) {\n    $articles_stream-\u003epush($url);\n}\nforeach ($articles_stream-\u003egetSitemaps() as $sitemap) {\n    $index_stream-\u003epushSitemap($sitemap);\n}\n$articles_stream-\u003eclose();\n\n// similarly for main stream\n$main_stream-\u003eopen();\nforeach ($main_urls as $url) {\n    $main_stream-\u003epush($url);\n}\nforeach ($main_stream-\u003egetSitemaps() as $sitemap) {\n    $index_stream-\u003epushSitemap($sitemap);\n}\n$main_stream-\u003eclose();\n\n// finish create index\n$index_stream-\u003eclose();\n```\n\nAs a result, you will get a file structure like this:\n\n```\nsitemap.xml\nsitemap_news1.xml\nsitemap_news2.xml\nsitemap_news3.xml\nsitemap_articles1.xml\nsitemap_articles2.xml\nsitemap_articles3.xml\nsitemap_main1.xml\nsitemap_main2.xml\nsitemap_main3.xml\n```\n\n## Streams\n\n * `MultiStream` - allows to use multiple streams as one;\n * `WritingStream` - use [`Writer`](#Writer) for write a Sitemap;\n * `WritingIndexStream` - writes a Sitemap index with [`Writer`](#Writer);\n * `WritingSplitIndexStream` - split list URLs to sitemap parts and write its with [`Writer`](#Writer) to a Sitemap\n index;\n * `WritingSplitStream` - split list URLs and write its with [`Writer`](#Writer) to a Sitemaps;\n * `OutputStream` - sends a Sitemap to the output buffer. You can use it\n[in controllers](https://symfony.com/doc/current/components/http_foundation.html#streaming-a-response);\n * `LoggerStream` - use\n [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) for log added URLs.\n\nYou can use a composition of streams.\n\n```php\n$stream = new MultiStream(\n    new LoggerStream(/* $logger */),\n    new WritingSplitIndexStream(\n        new PlainTextSitemapIndexRender(),\n        new PlainTextSitemapRender(),\n        new TempFileWriter(),\n        new GzipTempFileWriter(9),\n         __DIR__.'/sitemap.xml',\n         __DIR__.'/sitemap%d.xml.gz',\n        'https://example.com/sitemap%d.xml.gz'\n    )\n);\n```\n\nStreaming to file and compress result without index.\n\n```php\n$render = new PlainTextSitemapRender();\n\n$stream = new MultiStream(\n    new LoggerStream(/* $logger */),\n    new WritingStream($render, new GzipTempFileWriter(9), __DIR__.'/sitemap.xml.gz'),\n    new WritingStream($render, new TempFileWriter(), __DIR__.'/sitemap.xml')\n);\n```\n\nStreaming to file and output buffer.\n\n```php\n$render = new PlainTextSitemapRender();\n\n$stream = new MultiStream(\n    new LoggerStream(/* $logger */),\n    new WritingStream($render, new TempFileWriter(), __DIR__.'/sitemap.xml'),\n    new OutputStream($render)\n);\n```\n\n## Writer\n\n * `FileWriter` - write a Sitemap to the file;\n * `TempFileWriter` - write a Sitemap to the temporary file and move in to target directory after finish writing;\n * `GzipFileWriter` - write a Sitemap to the file compressed by gzip;\n * `GzipTempFileWriter` - write a Sitemap to the temporary file compressed by gzip and move in to target directory\n after finish writing.\n * `DeflateFileWriter` - write a Sitemap to the file compressed by deflate;\n * `DeflateTempFileWriter` - write a Sitemap to the temporary file compressed by deflate and move in to target\n directory after finish writing.\n\n## Render\n\nIf you install the [XMLWriter](https://www.php.net/manual/en/book.xmlwriter.php) PHP extension, you can use\n`XMLWriterSitemapRender` and `XMLWriterSitemapIndexRender`. Otherwise you can use `PlainTextSitemapRender` and\n`PlainTextSitemapIndexRender` who do not require any dependencies and are more economical.\n\n## The location of Sitemap file\n\nThe Sitemap protocol imposes restrictions on the URLs that can be specified in it, depending on the location of the\nSitemap file:\n\n * All URLs listed in the Sitemap must use the same protocol (`https`, in this example) and reside on\n   the same host as the Sitemap. For instance, if the Sitemap is located at `https://www.example.com/sitemap.xml`, it\n   can't include URLs from `http://www.example.com/` or `https://subdomain.example.com`.\n * The location of a Sitemap file determines the set of URLs that can be included in that Sitemap. A Sitemap file\n   located at `https://example.com/catalog/sitemap.xml` can include any URLs starting with\n   `https://example.com/catalog/` but can not include URLs starting with `https://example.com/news/`.\n * If you submit a Sitemap using a path with a port number, you must include that port number as part of the path in\n   each URL listed in the Sitemap file. For instance, if your Sitemap is located at\n   `http://www.example.com:100/sitemap.xml`, then each URL listed in the Sitemap must begin with\n   `http://www.example.com:100`.\n * A Sitemap index file can only specify Sitemaps that are found on the same site as the Sitemap index file. For\n    example, `https://www.yoursite.com/sitemap_index.xml` can include Sitemaps on `https://www.yoursite.com` but not on\n    `http://www.yoursite.com`, `https://www.example.com` or `https://yourhost.yoursite.com`.\n\nURLs that are not considered valid may be dropped from further consideration by search bots. We do not check\nthese restrictions to improve performance and because we trust the developers, but you can enable checking for these\nrestrictions with the appropriate decorators. It is better to detect a problem during the sitemap build process than\nduring indexing.\n\n* `ScopeTrackingStream` - `Stream` decorator;\n* `ScopeTrackingSplitStream` - `SplitStream` decorator;\n* `ScopeTrackingIndexStream` - `IndexStream` decorator.\n\nThe decorators takes the stream to decorate and the sitemap scope as arguments.\n\n```php\n// file into which we will write a sitemap\n$filename = __DIR__.'/catalog/sitemap.xml';\n\n// configure stream\n$render = new PlainTextSitemapRender();\n$writer = new TempFileWriter();\n$wrapped_stream = new WritingStream($render, $writer, $filename);\n\n// all URLs not started with this path will be considered invalid\n$scope = 'https://example.com/catalog/';\n\n// decorate stream\n$stream = new ScopeTrackingStream($wrapped_stream, $scope);\n\n// build sitemap.xml\n$stream-\u003eopen();\n// this is a valid URLs\n$stream-\u003epush(Url::create('https://example.com/catalog/'));\n$stream-\u003epush(Url::create('https://example.com/catalog/123-my_product.html'));\n$stream-\u003epush(Url::create('https://example.com/catalog/brand/'));\n// using these URLs will throw exception\n//$stream-\u003epush(Url::create('https://example.com/')); // parent path\n//$stream-\u003epush(Url::create('https://example.com/news/')); // another path\n//$stream-\u003epush(Url::create('http://example.com/catalog/')); // another scheme\n//$stream-\u003epush(Url::create('https://example.com:80/catalog/')); // another port\n//$stream-\u003epush(Url::create('https://example.org/catalog/')); // another domain\n$stream-\u003eclose();\n```\n\n## License\n\nThis bundle is under the [MIT license](https://opensource.org/licenses/MIT). See the complete license in the file:\nLICENSE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgpslab%2Fsitemap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgpslab%2Fsitemap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgpslab%2Fsitemap/lists"}