From bb86699d13c7282c7bb8c29d1bbb3bde48f2fffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= Date: Fri, 21 Jul 2023 15:54:21 +0200 Subject: [PATCH] [!!!][FEATURE] Support multiple sitemaps to be located Related: #199 --- .../ItemProviders/CacheWarmupProvider.php | 24 +++- Classes/Cache/SitemapsCache.php | 60 +++++--- Classes/Command/WarmupCommand.php | 9 +- Classes/Controller/FetchSitesController.php | 28 ++-- Classes/Service/CacheWarmupService.php | 4 +- Classes/Sitemap/Provider/DefaultProvider.php | 6 +- Classes/Sitemap/Provider/PageTypeProvider.php | 9 +- Classes/Sitemap/Provider/Provider.php | 5 +- .../Sitemap/Provider/RobotsTxtProvider.php | 21 +-- Classes/Sitemap/Provider/SiteProvider.php | 8 +- Classes/Sitemap/SitemapLocator.php | 44 +++--- Documentation/DeveloperCorner/Caching.rst | 10 +- .../DeveloperCorner/SitemapProviders.rst | 19 ++- Documentation/Migration/Index.rst | 3 + .../Partials/Modal/Sites/SiteGroup.html | 8 +- Tests/Functional/Cache/SitemapsCacheTest.php | 132 +++++++++++++----- .../Sitemap/Provider/PageTypeProviderTest.php | 32 +++-- .../Functional/Sitemap/SitemapLocatorTest.php | 79 +++++++---- .../Sitemap/Provider/DefaultProviderTest.php | 12 +- .../Provider/RobotsTxtProviderTest.php | 32 +++-- .../Sitemap/Provider/SiteProviderTest.php | 31 ++-- 21 files changed, 375 insertions(+), 201 deletions(-) diff --git a/Classes/Backend/ContextMenu/ItemProviders/CacheWarmupProvider.php b/Classes/Backend/ContextMenu/ItemProviders/CacheWarmupProvider.php index a31e47861..c14453c31 100644 --- a/Classes/Backend/ContextMenu/ItemProviders/CacheWarmupProvider.php +++ b/Classes/Backend/ContextMenu/ItemProviders/CacheWarmupProvider.php @@ -26,6 +26,7 @@ use EliasHaeussler\Typo3Warming\Configuration; use EliasHaeussler\Typo3Warming\Sitemap; use EliasHaeussler\Typo3Warming\Utility; +use Exception; use TYPO3\CMS\Backend; use TYPO3\CMS\Core; @@ -239,10 +240,25 @@ private function canWarmupCachesOfSite(Core\Site\Entity\SiteLanguage $siteLangua $site = $this->getCurrentSite(); $languageId = $siteLanguage?->getLanguageId(); - return $site !== null - && $site->getRootPageId() === (int)$this->identifier - && Utility\AccessUtility::canWarmupCacheOfSite($site, $languageId) - && $this->sitemapLocator->siteContainsSitemap($site, $siteLanguage); + if ($site === null || + $site->getRootPageId() !== (int)$this->identifier || + !Utility\AccessUtility::canWarmupCacheOfSite($site, $languageId) + ) { + return false; + } + + // Check if any sitemap exists + try { + foreach ($this->sitemapLocator->locateBySite($site, $siteLanguage) as $sitemap) { + if ($this->sitemapLocator->sitemapExists($sitemap)) { + return true; + } + } + } catch (Exception) { + // Unable to locate any sitemaps + } + + return false; } private function getCurrentSite(): ?Core\Site\Entity\Site diff --git a/Classes/Cache/SitemapsCache.php b/Classes/Cache/SitemapsCache.php index 9df33f002..24a8ae086 100644 --- a/Classes/Cache/SitemapsCache.php +++ b/Classes/Cache/SitemapsCache.php @@ -43,40 +43,54 @@ public function __construct( } /** + * @return list * @throws Exception\InvalidUrlException */ public function get( Core\Site\Entity\Site $site, Core\Site\Entity\SiteLanguage $siteLanguage = null, - ): Sitemap\SiteAwareSitemap|null { - /** @var array>|false $cacheData */ + ): array { + /** @var array>>|false $cacheData */ $cacheData = $this->cache->require(self::ENTRY_IDENTIFIER); // Early return if cache is empty if ($cacheData === false) { - return null; + return []; } - // Fetch sitemap from cache data + // Fetch sitemaps from cache data $siteIdentifier = $site->getIdentifier(); $languageIdentifier = $this->buildLanguageIdentifier($site, $siteLanguage); - $sitemap = $cacheData[$siteIdentifier][$languageIdentifier] ?? null; + $sitemaps = $cacheData[$siteIdentifier][$languageIdentifier] ?? null; - // Early return if sitemap is not cached - if (!\is_string($sitemap)) { - return null; + // BC: Convert single sitemap to sitemaps array + if (\is_string($sitemaps)) { + $sitemaps = [$sitemaps]; } - return new Sitemap\SiteAwareSitemap( - new Core\Http\Uri($sitemap), - $site, - $siteLanguage ?? $site->getDefaultLanguage(), + // Early return if sitemaps are not cached + if (!\is_array($sitemaps)) { + return []; + } + + return array_values( + array_map( + static fn (string $sitemapUrl) => new Sitemap\SiteAwareSitemap( + new Core\Http\Uri($sitemapUrl), + $site, + $siteLanguage ?? $site->getDefaultLanguage(), + ), + array_filter($sitemaps, 'is_string'), + ), ); } - public function set(Sitemap\SiteAwareSitemap $sitemap): void + /** + * @param list $sitemaps + */ + public function set(array $sitemaps): void { - /** @var array>|false $cacheData */ + /** @var array>>|false $cacheData */ $cacheData = $this->cache->require(self::ENTRY_IDENTIFIER); // Enforce array for cached data @@ -84,10 +98,20 @@ public function set(Sitemap\SiteAwareSitemap $sitemap): void $cacheData = []; } - // Append sitemap url to cache data - $siteIdentifier = $sitemap->getSite()->getIdentifier(); - $languageIdentifier = $this->buildLanguageIdentifier($sitemap->getSite(), $sitemap->getSiteLanguage()); - $cacheData[$siteIdentifier][$languageIdentifier] = (string)$sitemap->getUri(); + // Append sitemap urls to cache data + foreach ($sitemaps as $sitemap) { + $siteIdentifier = $sitemap->getSite()->getIdentifier(); + $languageIdentifier = $this->buildLanguageIdentifier($sitemap->getSite(), $sitemap->getSiteLanguage()); + $cachedUrls = $cacheData[$siteIdentifier][$languageIdentifier] ?? []; + + // BC: Convert single sitemap to sitemaps array + if (\is_string($cachedUrls)) { + $cachedUrls = [$cachedUrls]; + } + + $cachedUrls[] = (string)$sitemap->getUri(); + $cacheData[$siteIdentifier][$languageIdentifier] = $cachedUrls; + } $this->cache->set( self::ENTRY_IDENTIFIER, diff --git a/Classes/Command/WarmupCommand.php b/Classes/Command/WarmupCommand.php index 91ce83e88..b9db34147 100644 --- a/Classes/Command/WarmupCommand.php +++ b/Classes/Command/WarmupCommand.php @@ -334,10 +334,11 @@ private function resolveSites(array $sites, array $languages): array } foreach ($languageIds as $languageId) { - $resolvedSitemaps[] = (string)$this->sitemapLocator->locateBySite( - $site, - $site->getLanguageById($languageId) - )->getUri(); + $sitemaps = $this->sitemapLocator->locateBySite($site, $site->getLanguageById($languageId)); + + foreach ($sitemaps as $sitemap) { + $resolvedSitemaps[] = (string)$sitemap->getUri(); + } } } } diff --git a/Classes/Controller/FetchSitesController.php b/Classes/Controller/FetchSitesController.php index d061e6224..ea1077149 100644 --- a/Classes/Controller/FetchSitesController.php +++ b/Classes/Controller/FetchSitesController.php @@ -93,20 +93,22 @@ private function createSiteGroup(Core\Site\Entity\Site $site, array $page): ?Val $items = []; // Check all available languages for possible sitemaps - foreach ($this->sitemapLocator->locateAllBySite($site) as $sitemap) { - $siteLanguage = $sitemap->getSiteLanguage(); - $url = null; - - // Check if sitemap is accessible - if ($this->sitemapLocator->siteContainsSitemap($site, $siteLanguage)) { - $url = (string)$sitemap->getUri(); + foreach ($this->sitemapLocator->locateAllBySite($site) as $i => $sitemaps) { + foreach ($sitemaps as $sitemap) { + $siteLanguage = $sitemap->getSiteLanguage(); + $url = null; + + // Check if sitemap exists + if ($this->sitemapLocator->sitemapExists($sitemap)) { + $url = (string)$sitemap->getUri(); + } + + $items[] = new ValueObject\Modal\SiteGroupItem( + $siteLanguage, + $siteLanguage === $site->getDefaultLanguage(), + $url, + ); } - - $items[] = new ValueObject\Modal\SiteGroupItem( - $siteLanguage, - $siteLanguage === $site->getDefaultLanguage(), - $url, - ); } // Early return if no languages are available diff --git a/Classes/Service/CacheWarmupService.php b/Classes/Service/CacheWarmupService.php index e61b782d4..ef8abf13a 100644 --- a/Classes/Service/CacheWarmupService.php +++ b/Classes/Service/CacheWarmupService.php @@ -88,8 +88,8 @@ public function warmup( foreach ($sites as $siteWarmupRequest) { foreach ($siteWarmupRequest->getLanguageIds() as $languageId) { $siteLanguage = $siteWarmupRequest->getSite()->getLanguageById($languageId); - $sitemap = $this->sitemapLocator->locateBySite($siteWarmupRequest->getSite(), $siteLanguage); - $cacheWarmer->addSitemaps($sitemap); + $sitemaps = $this->sitemapLocator->locateBySite($siteWarmupRequest->getSite(), $siteLanguage); + $cacheWarmer->addSitemaps($sitemaps); } } diff --git a/Classes/Sitemap/Provider/DefaultProvider.php b/Classes/Sitemap/Provider/DefaultProvider.php index 553fa96aa..b043bc871 100644 --- a/Classes/Sitemap/Provider/DefaultProvider.php +++ b/Classes/Sitemap/Provider/DefaultProvider.php @@ -40,12 +40,14 @@ final class DefaultProvider implements Provider public function get( Core\Site\Entity\Site $site, Core\Site\Entity\SiteLanguage $siteLanguage = null, - ): ?Sitemap\SiteAwareSitemap { - return new Sitemap\SiteAwareSitemap( + ): array { + $sitemap = new Sitemap\SiteAwareSitemap( Utility\HttpUtility::getSiteUrlWithPath($site, self::DEFAULT_PATH, $siteLanguage), $site, $siteLanguage ?? $site->getDefaultLanguage(), ); + + return [$sitemap]; } /** diff --git a/Classes/Sitemap/Provider/PageTypeProvider.php b/Classes/Sitemap/Provider/PageTypeProvider.php index 1c4825701..d0f813f1f 100644 --- a/Classes/Sitemap/Provider/PageTypeProvider.php +++ b/Classes/Sitemap/Provider/PageTypeProvider.php @@ -42,25 +42,26 @@ final class PageTypeProvider implements Provider public function get( Core\Site\Entity\Site $site, Core\Site\Entity\SiteLanguage $siteLanguage = null, - ): ?Sitemap\SiteAwareSitemap { + ): array { // Early return if EXT:seo is not installed if (!Core\Utility\ExtensionManagementUtility::isLoaded('seo')) { - return null; + return []; } $pageTypeMap = $site->getConfiguration()['routeEnhancers']['PageTypeSuffix']['map'] ?? null; // Early return if no page type map is configured if (!\is_array($pageTypeMap) || !\in_array(self::EXPECTED_PAGE_TYPE, $pageTypeMap, true)) { - return null; + return []; } $uri = $site->getRouter()->generateUri('/', [ 'type' => self::EXPECTED_PAGE_TYPE, '_language' => $siteLanguage, ]); + $sitemap = new Sitemap\SiteAwareSitemap($uri, $site, $siteLanguage ?? $site->getDefaultLanguage()); - return new Sitemap\SiteAwareSitemap($uri, $site, $siteLanguage ?? $site->getDefaultLanguage()); + return [$sitemap]; } /** diff --git a/Classes/Sitemap/Provider/Provider.php b/Classes/Sitemap/Provider/Provider.php index f7b7238da..d9fddacbc 100644 --- a/Classes/Sitemap/Provider/Provider.php +++ b/Classes/Sitemap/Provider/Provider.php @@ -34,10 +34,13 @@ */ interface Provider { + /** + * @return list + */ public function get( Core\Site\Entity\Site $site, Core\Site\Entity\SiteLanguage $siteLanguage = null, - ): ?Sitemap\SiteAwareSitemap; + ): array; public static function getPriority(): int; } diff --git a/Classes/Sitemap/Provider/RobotsTxtProvider.php b/Classes/Sitemap/Provider/RobotsTxtProvider.php index 49232188f..998d4de97 100644 --- a/Classes/Sitemap/Provider/RobotsTxtProvider.php +++ b/Classes/Sitemap/Provider/RobotsTxtProvider.php @@ -47,24 +47,29 @@ public function __construct( public function get( Core\Site\Entity\Site $site, Core\Site\Entity\SiteLanguage $siteLanguage = null, - ): ?Sitemap\SiteAwareSitemap { + ): array { $robotsTxtUri = Utility\HttpUtility::getSiteUrlWithPath($site, 'robots.txt', $siteLanguage); $robotsTxt = $this->fetchRobotsTxt($robotsTxtUri); // Early return if no robots.txt exists if ($robotsTxt === null || trim($robotsTxt) === '') { - return null; + return []; } // Early return if no sitemap is specified in robots.txt - if (preg_match(self::SITEMAP_PATTERN, $robotsTxt, $matches) !== 1) { - return null; + if ((int)preg_match_all(self::SITEMAP_PATTERN, $robotsTxt, $matches) === 0) { + return []; } - return new Sitemap\SiteAwareSitemap( - new Core\Http\Uri($matches['url']), - $site, - $siteLanguage ?? $site->getDefaultLanguage(), + return array_values( + array_map( + static fn (string $url) => new Sitemap\SiteAwareSitemap( + new Core\Http\Uri($url), + $site, + $siteLanguage ?? $site->getDefaultLanguage(), + ), + $matches['url'], + ), ); } diff --git a/Classes/Sitemap/Provider/SiteProvider.php b/Classes/Sitemap/Provider/SiteProvider.php index 1dae3bbda..f57b6a589 100644 --- a/Classes/Sitemap/Provider/SiteProvider.php +++ b/Classes/Sitemap/Provider/SiteProvider.php @@ -38,7 +38,7 @@ final class SiteProvider implements Provider public function get( Core\Site\Entity\Site $site, Core\Site\Entity\SiteLanguage $siteLanguage = null, - ): ?Sitemap\SiteAwareSitemap { + ): array { if ($siteLanguage !== null && $siteLanguage !== $site->getDefaultLanguage()) { $sitemapPath = $siteLanguage->toArray()['xml_sitemap_path'] ?? null; } else { @@ -47,14 +47,16 @@ public function get( // Early return if no sitemap path is configured if (!\is_string($sitemapPath) || trim($sitemapPath) === '') { - return null; + return []; } - return new Sitemap\SiteAwareSitemap( + $sitemap = new Sitemap\SiteAwareSitemap( Utility\HttpUtility::getSiteUrlWithPath($site, $sitemapPath, $siteLanguage), $site, $siteLanguage ?? $site->getDefaultLanguage(), ); + + return [$sitemap]; } /** diff --git a/Classes/Sitemap/SitemapLocator.php b/Classes/Sitemap/SitemapLocator.php index cdf812c90..b2afcec46 100644 --- a/Classes/Sitemap/SitemapLocator.php +++ b/Classes/Sitemap/SitemapLocator.php @@ -50,6 +50,7 @@ public function __construct( } /** + * @return list * @throws CacheWarmup\Exception\InvalidUrlException * @throws Exception\UnsupportedConfigurationException * @throws Exception\UnsupportedSiteException @@ -57,10 +58,10 @@ public function __construct( public function locateBySite( Core\Site\Entity\Site $site, Core\Site\Entity\SiteLanguage $siteLanguage = null, - ): SiteAwareSitemap { - // Get sitemap from cache - if (($sitemap = $this->cache->get($site, $siteLanguage)) !== null) { - return $sitemap; + ): array { + // Get sitemaps from cache + if (($sitemaps = $this->cache->get($site, $siteLanguage)) !== []) { + return $sitemaps; } // Build and validate base URL @@ -69,20 +70,20 @@ public function locateBySite( throw Exception\UnsupportedConfigurationException::forBaseUrl((string)$baseUrl); } - // Resolve and validate sitemap - $sitemap = $this->resolveSitemap($site, $siteLanguage); - if ($sitemap === null) { + // Resolve and validate sitemaps + $sitemaps = $this->resolveSitemaps($site, $siteLanguage); + if ($sitemaps === []) { throw Exception\UnsupportedSiteException::forMissingSitemap($site); } - // Store resolved sitemap in cache - $this->cache->set($sitemap); + // Store resolved sitemaps in cache + $this->cache->set($sitemaps); - return $sitemap; + return $sitemaps; } /** - * @return array + * @return array> * @throws CacheWarmup\Exception\InvalidUrlException * @throws Exception\UnsupportedConfigurationException * @throws Exception\UnsupportedSiteException @@ -100,13 +101,9 @@ public function locateAllBySite(Core\Site\Entity\Site $site): array return $sitemaps; } - // @todo think about the locate <> contains behavior - public function siteContainsSitemap( - Core\Site\Entity\Site $site, - Core\Site\Entity\SiteLanguage $siteLanguage = null, - ): bool { + public function sitemapExists(SiteAwareSitemap $sitemap): bool + { try { - $sitemap = $this->locateBySite($site, $siteLanguage); $response = $this->requestFactory->request((string)$sitemap->getUri(), 'HEAD'); return $response->getStatusCode() < 400; @@ -115,17 +112,20 @@ public function siteContainsSitemap( } } - private function resolveSitemap( + /** + * @return list + */ + private function resolveSitemaps( Core\Site\Entity\Site $site, Core\Site\Entity\SiteLanguage $siteLanguage = null, - ): ?SiteAwareSitemap { + ): array { foreach ($this->providers as $provider) { - if (($sitemap = $provider->get($site, $siteLanguage)) !== null) { - return $sitemap; + if (($sitemaps = $provider->get($site, $siteLanguage)) !== []) { + return $sitemaps; } } - return null; + return []; } /** diff --git a/Documentation/DeveloperCorner/Caching.rst b/Documentation/DeveloperCorner/Caching.rst index 488aad9fc..2b337bb22 100644 --- a/Documentation/DeveloperCorner/Caching.rst +++ b/Documentation/DeveloperCorner/Caching.rst @@ -19,17 +19,17 @@ which defaults to a filesystem cache located at :file:`var/cache/code/warming/si .. php:method:: get($site, $siteLanguage = null) - Get the located sitemap of a given site. + Get the located sitemaps of a given site. :param TYPO3\\CMS\\Core\\Site\\Entity\\Site $site: The sitemap's site object. :param TYPO3\\CMS\\Core\\Site\\Entity\\SiteLanguage $siteLanguage: An optional site language. - :returns: Located sitemap of a given site. + :returns: Located sitemaps of a given site. - .. php:method:: set($sitemap) + .. php:method:: set($sitemaps) - Add the located sitemap to the `warming` cache. + Add the located sitemaps to the `warming` cache. - :param EliasHaeussler\\Typo3Warming\\Sitemap\\SiteAwareSitemap $sitemap: The located sitemap to be cached. + :param array $sitemaps: The located sitemaps to be cached. .. seealso:: diff --git a/Documentation/DeveloperCorner/SitemapProviders.rst b/Documentation/DeveloperCorner/SitemapProviders.rst index cca2782d0..d48a91fdd 100644 --- a/Documentation/DeveloperCorner/SitemapProviders.rst +++ b/Documentation/DeveloperCorner/SitemapProviders.rst @@ -19,11 +19,11 @@ priority) to localize XML sitemaps according to certain criteria. .. php:method:: get($site, $siteLanguage = null) - Locate the XML sitemap path of the given site. + Locate the XML sitemaps of the given site. :param TYPO3\\CMS\\Core\\Site\\Entity\\Site $site: The site whose XML sitemap path should be located. :param TYPO3\\CMS\\Core\\Site\\Entity\\SiteLanguage $siteLanguage: An optional site language to include while locating the XML sitemap path. - :returns: An instance of :php:class:`EliasHaeussler\\Typo3Warming\\Sitemap\\SiteAwareSitemap` or :php:`null`. + :returns: An array of instances of :php:class:`EliasHaeussler\\Typo3Warming\\Sitemap\\SiteAwareSitemap`. .. php:staticmethod:: getPriority() @@ -63,17 +63,14 @@ By default, the path to an XML sitemap is determined in three steps: Read more at `sitemaps.org `__. .. note:: - Only the first occurrence will be respected. In the following - example, the resulting sitemap is - `https://www.example.com/our-sitemap.xml`. - .. code-block:: none - :emphasize-lines: 3 + .. versionadded:: 1.0.0 - User-agent: * - Disallow: /tmp/ - Sitemap: https://www.example.com/our-sitemap.xml - Sitemap: https://www.example.com/our-other-sitemap.xml + `Feature #336 – Support multiple sitemaps to be located `__ + + Since version 1.0.0 of EXT:warming, all sitemaps in `robots.txt` + are captured. Prior to this version, only the first occurrence was + respected. 4. Default path diff --git a/Documentation/Migration/Index.rst b/Documentation/Migration/Index.rst index 6df846068..09b9c0241 100644 --- a/Documentation/Migration/Index.rst +++ b/Documentation/Migration/Index.rst @@ -75,6 +75,9 @@ Sitemap providers :php:interface:`EliasHaeussler\\Typo3Warming\\Sitemap\\Provider\\Provider` directly. The previously available trait method is now available within :php:meth:`EliasHaeussler\\Typo3Warming\\Utility\\HttpUtility::getSiteUrlWithPath`. +- :php:meth:`EliasHaeussler\\Typo3Warming\\Sitemap\\Provider\\Provider::get` + now returns an array of :php:class:`EliasHaeussler\\Typo3Warming\\Sitemap\\SiteAwareSitemap` + instances. - A new sitemap provider :php:class:`EliasHaeussler\\Typo3Warming\\Sitemap\\Provider\\PageTypeProvider` was added. It is configured with highest priority. Read more at diff --git a/Resources/Private/Partials/Modal/Sites/SiteGroup.html b/Resources/Private/Partials/Modal/Sites/SiteGroup.html index 5515e72ae..fc2468d8a 100644 --- a/Resources/Private/Partials/Modal/Sites/SiteGroup.html +++ b/Resources/Private/Partials/Modal/Sites/SiteGroup.html @@ -87,12 +87,12 @@ }" /> - +
subject->get($site)); + self::assertSame([], $this->subject->get($site)); } #[Framework\Attributes\Test] - public function getReturnsNullIfGivenSiteIsNotCached(): void + public function getReturnsEmptyArrayIfGivenSiteIsNotCached(): void { $site = new Core\Site\Entity\Site('baz', 1, []); $this->cache->set('sitemaps', 'return [];'); - self::assertNull($this->subject->get($site)); + self::assertSame([], $this->subject->get($site)); } #[Framework\Attributes\Test] - public function getReturnsCachedSitemapForDefaultLanguage(): void + public function getReturnsCachedSitemapsForDefaultLanguage(): void { $site = new Core\Site\Entity\Site('foo', 1, []); @@ -98,7 +98,10 @@ public function getReturnsCachedSitemapForDefaultLanguage(): void var_export( [ 'foo' => [ - 'default' => 'https://www.example.com/baz', + 'default' => [ + 'https://www.example.com/baz', + 'https://www.example.com/bar', + ], ], ], true, @@ -106,16 +109,55 @@ public function getReturnsCachedSitemapForDefaultLanguage(): void ), ); - $expected = new Src\Sitemap\SiteAwareSitemap( - new Core\Http\Uri('https://www.example.com/baz'), - $site, - $site->getDefaultLanguage(), - ); + $expected = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/baz'), + $site, + $site->getDefaultLanguage(), + ), + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/bar'), + $site, + $site->getDefaultLanguage(), + ), + ]; self::assertEquals($expected, $this->subject->get($site)); self::assertEquals($expected, $this->subject->get($site, $site->getDefaultLanguage())); } + #[Framework\Attributes\Test] + public function getReturnsCachedSitemapsUsingBackwardsCompatibilityLayer(): void + { + $site = new Core\Site\Entity\Site('foo', 1, []); + + $this->cache->set( + 'sitemaps', + sprintf( + 'return %s;', + var_export( + [ + 'foo' => [ + // BC: string will be converted to array + 'default' => 'https://www.example.com/baz', + ], + ], + true, + ), + ), + ); + + $expected = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/baz'), + $site, + $site->getDefaultLanguage(), + ), + ]; + + self::assertEquals($expected, $this->subject->get($site)); + } + #[Framework\Attributes\Test] public function getReturnsCachedSitemapForGivenLanguage(): void { @@ -129,7 +171,10 @@ public function getReturnsCachedSitemapForGivenLanguage(): void var_export( [ 'foo' => [ - '1' => 'https://www.example.com/baz', + '1' => [ + 'https://www.example.com/baz', + 'https://www.example.com/bar', + ], ], ], true, @@ -137,11 +182,18 @@ public function getReturnsCachedSitemapForGivenLanguage(): void ), ); - $expected = new Src\Sitemap\SiteAwareSitemap( - new Core\Http\Uri('https://www.example.com/baz'), - $site, - $siteLanguage, - ); + $expected = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/baz'), + $site, + $siteLanguage, + ), + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/bar'), + $site, + $siteLanguage, + ), + ]; self::assertEquals($expected, $this->subject->get($site, $siteLanguage)); } @@ -154,15 +206,22 @@ public function setInitializesCacheIfCacheIsMissing(): void self::assertFalse($this->cache->get('sitemaps')); $site = new Core\Site\Entity\Site('foo', 1, []); - $sitemap = new Src\Sitemap\SiteAwareSitemap( - new Core\Http\Uri('https://www.example.com/baz'), - $site, - $site->getDefaultLanguage(), - ); + $sitemaps = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/baz'), + $site, + $site->getDefaultLanguage(), + ), + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/bar'), + $site, + $site->getDefaultLanguage(), + ), + ]; - $this->subject->set($sitemap); + $this->subject->set($sitemaps); - self::assertEquals($sitemap, $this->subject->get($site, $site->getDefaultLanguage())); + self::assertEquals($sitemaps, $this->subject->get($site, $site->getDefaultLanguage())); self::assertNotFalse($this->cache->get('sitemaps')); } @@ -170,15 +229,22 @@ public function setInitializesCacheIfCacheIsMissing(): void public function setStoresGivenSitemapInCache(): void { $site = new Core\Site\Entity\Site('foo', 1, []); - $sitemap = new Src\Sitemap\SiteAwareSitemap( - new Core\Http\Uri('https://www.example.com/baz'), - $site, - $site->getDefaultLanguage(), - ); + $sitemaps = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/baz'), + $site, + $site->getDefaultLanguage(), + ), + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/bar'), + $site, + $site->getDefaultLanguage(), + ), + ]; - $this->subject->set($sitemap); + $this->subject->set($sitemaps); - self::assertEquals($sitemap, $this->subject->get($site, $site->getDefaultLanguage())); + self::assertEquals($sitemaps, $this->subject->get($site, $site->getDefaultLanguage())); } private function removeCache(): void diff --git a/Tests/Functional/Sitemap/Provider/PageTypeProviderTest.php b/Tests/Functional/Sitemap/Provider/PageTypeProviderTest.php index 9b7cf55d5..5b03045b5 100644 --- a/Tests/Functional/Sitemap/Provider/PageTypeProviderTest.php +++ b/Tests/Functional/Sitemap/Provider/PageTypeProviderTest.php @@ -52,21 +52,21 @@ protected function setUp(): void } #[Framework\Attributes\Test] - public function getReturnsNullIfSeoExtensionIsNotLoaded(): void + public function getReturnsEmptyArrayIfSeoExtensionIsNotLoaded(): void { $site = new Core\Site\Entity\Site('foo', 1, []); - self::assertNull($this->subject->get($site)); + self::assertSame([], $this->subject->get($site)); } #[Framework\Attributes\Test] - public function getReturnsNullIfPageTypeIsNotConfigured(): void + public function getReturnsEmptyArrayIfPageTypeIsNotConfigured(): void { $this->packageManager->loadedExtensions = ['seo']; $site = new Core\Site\Entity\Site('foo', 1, []); - self::assertNull($this->subject->get($site)); + self::assertSame([], $this->subject->get($site)); } #[Framework\Attributes\Test] @@ -86,11 +86,13 @@ public function getReturnsSitemapWithPageTypeFromSite(): void ], ]); - $expected = new Src\Sitemap\SiteAwareSitemap( - new Core\Http\Uri('https://www.example.com/baz.xml'), - $site, - $site->getDefaultLanguage(), - ); + $expected = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/baz.xml'), + $site, + $site->getDefaultLanguage(), + ), + ]; self::assertEquals($expected, $this->subject->get($site)); } @@ -113,11 +115,13 @@ public function getReturnsSitemapWithPageTypeAndSiteLanguage(): void ]); $siteLanguage = new Core\Site\Entity\SiteLanguage(1, 'de_DE.UTF-8', new Core\Http\Uri('https://www.example.com/de/'), []); - $expected = new Src\Sitemap\SiteAwareSitemap( - new Core\Http\Uri('https://www.example.com/de/baz.xml'), - $site, - $siteLanguage, - ); + $expected = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/de/baz.xml'), + $site, + $siteLanguage, + ), + ]; self::assertEquals($expected, $this->subject->get($site, $siteLanguage)); } diff --git a/Tests/Functional/Sitemap/SitemapLocatorTest.php b/Tests/Functional/Sitemap/SitemapLocatorTest.php index 34685cff2..5934302d6 100644 --- a/Tests/Functional/Sitemap/SitemapLocatorTest.php +++ b/Tests/Functional/Sitemap/SitemapLocatorTest.php @@ -101,15 +101,17 @@ public function constructorThrowsExceptionIfGivenProviderIsNoValidObject(): void public function locateBySiteReturnsCachedSitemap(?Core\Site\Entity\SiteLanguage $siteLanguage, string $expectedUrl): void { $site = self::getSite([]); - $sitemap = new Src\Sitemap\SiteAwareSitemap( - new Core\Http\Uri($expectedUrl), - $site, - $siteLanguage ?? $site->getDefaultLanguage(), - ); + $sitemaps = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri($expectedUrl), + $site, + $siteLanguage ?? $site->getDefaultLanguage(), + ), + ]; - $this->cache->set($sitemap); + $this->cache->set($sitemaps); - self::assertEquals($sitemap, $this->subject->locateBySite($site, $siteLanguage)); + self::assertEquals($sitemaps, $this->subject->locateBySite($site, $siteLanguage)); } #[Framework\Attributes\Test] @@ -148,13 +150,15 @@ public function locateBySiteThrowsExceptionIfProvidersCannotResolveSitemap(?Core public function locateBySiteReturnsLocatedSitemap(?Core\Site\Entity\SiteLanguage $siteLanguage, string $expectedUrl): void { $site = self::getSite(); - $sitemap = new Src\Sitemap\SiteAwareSitemap( - new Core\Http\Uri($expectedUrl), - $site, - $siteLanguage ?? $site->getDefaultLanguage(), - ); + $sitemap = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri($expectedUrl), + $site, + $siteLanguage ?? $site->getDefaultLanguage(), + ), + ]; - self::assertNull($this->cache->get($site, $siteLanguage)); + self::assertSame([], $this->cache->get($site, $siteLanguage)); self::assertEquals($sitemap, $this->subject->locateBySite($site, $siteLanguage)); self::assertEquals($sitemap, $this->cache->get($site, $siteLanguage)); } @@ -211,39 +215,62 @@ public function locateAllBySiteExcludesInaccessibleLanguages(): void 1 => self::getSiteLanguage()->toArray(), ], ]); - $sitemap = new Src\Sitemap\SiteAwareSitemap( - new Core\Http\Uri('https://www.example.com/'), - $site, - $site->getLanguageById(1), - ); + $sitemaps = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/'), + $site, + $site->getLanguageById(1), + ), + ]; - $this->cache->set($sitemap); + $this->cache->set($sitemaps); - self::assertEquals([1 => $sitemap], $this->subject->locateAllBySite($site)); + self::assertEquals([1 => $sitemaps], $this->subject->locateAllBySite($site)); } #[Framework\Attributes\Test] - public function siteContainsSitemapReturnsFalseOnInaccessibleSitemap(): void + public function sitemapExistsReturnsFalseOnInaccessibleSitemap(): void { $this->requestFactory->exception = new Exception(); - self::assertFalse($this->subject->siteContainsSitemap(self::getSite())); + $site = self::getSite(); + $sitemap = new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/'), + $site, + $site->getLanguageById(1), + ); + + self::assertFalse($this->subject->sitemapExists($sitemap)); } #[Framework\Attributes\Test] - public function siteContainsSitemapReturnsFalseOnFailedRequest(): void + public function sitemapExistsReturnsFalseOnFailedRequest(): void { $this->requestFactory->response = new Core\Http\Response(null, 404); - self::assertFalse($this->subject->siteContainsSitemap(self::getSite())); + $site = self::getSite(); + $sitemap = new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/'), + $site, + $site->getLanguageById(1), + ); + + self::assertFalse($this->subject->sitemapExists($sitemap)); } #[Framework\Attributes\Test] - public function siteContainsSitemapReturnsTrueOnSuccessfulRequest(): void + public function sitemapExistsReturnsTrueOnSuccessfulRequest(): void { $this->requestFactory->response = new Core\Http\Response(); - self::assertTrue($this->subject->siteContainsSitemap(self::getSite())); + $site = self::getSite(); + $sitemap = new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/'), + $site, + $site->getLanguageById(1), + ); + + self::assertTrue($this->subject->sitemapExists($sitemap)); } /** diff --git a/Tests/Unit/Sitemap/Provider/DefaultProviderTest.php b/Tests/Unit/Sitemap/Provider/DefaultProviderTest.php index 49eae5652..74a59e0a5 100644 --- a/Tests/Unit/Sitemap/Provider/DefaultProviderTest.php +++ b/Tests/Unit/Sitemap/Provider/DefaultProviderTest.php @@ -49,11 +49,13 @@ protected function setUp(): void public function getReturnsSitemapWithDefaultPath(): void { $site = new Core\Site\Entity\Site('foo', 1, ['base' => 'https://www.example.com/']); - $expected = new Src\Sitemap\SiteAwareSitemap( - new Core\Http\Uri('https://www.example.com/sitemap.xml'), - $site, - $site->getDefaultLanguage(), - ); + $expected = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/sitemap.xml'), + $site, + $site->getDefaultLanguage(), + ), + ]; self::assertEquals($expected, $this->subject->get($site)); } diff --git a/Tests/Unit/Sitemap/Provider/RobotsTxtProviderTest.php b/Tests/Unit/Sitemap/Provider/RobotsTxtProviderTest.php index 272f5634c..2b8248b9f 100644 --- a/Tests/Unit/Sitemap/Provider/RobotsTxtProviderTest.php +++ b/Tests/Unit/Sitemap/Provider/RobotsTxtProviderTest.php @@ -53,15 +53,15 @@ protected function setUp(): void } #[Framework\Attributes\Test] - public function getReturnsNullIfNoRobotsTxtExists(): void + public function getReturnsEmptyArrayIfNoRobotsTxtExists(): void { $this->requestFactory->exception = new Exception(); - self::assertNull($this->subject->get($this->site)); + self::assertSame([], $this->subject->get($this->site)); } #[Framework\Attributes\Test] - public function getReturnsNullIfNoRobotsTxtDoesNotContainSitemapConfiguration(): void + public function getReturnsEmptyArrayIfNoRobotsTxtDoesNotContainSitemapConfiguration(): void { $response = new Core\Http\Response(); $body = $response->getBody(); @@ -70,7 +70,7 @@ public function getReturnsNullIfNoRobotsTxtDoesNotContainSitemapConfiguration(): $this->requestFactory->response = $response; - self::assertNull($this->subject->get($this->site)); + self::assertSame([], $this->subject->get($this->site)); } #[Framework\Attributes\Test] @@ -78,16 +78,28 @@ public function getReturnsSitemapIfRobotsTxtContainsSitemapConfiguration(): void { $response = new Core\Http\Response(); $body = $response->getBody(); - $body->write('Sitemap: https://www.example.com/baz.xml'); + $body->write( + <<rewind(); $this->requestFactory->response = $response; - $expected = new Src\Sitemap\SiteAwareSitemap( - new Core\Http\Uri('https://www.example.com/baz.xml'), - $this->site, - $this->site->getDefaultLanguage(), - ); + $expected = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/baz.xml'), + $this->site, + $this->site->getDefaultLanguage(), + ), + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri('https://www.example.com/bar.xml'), + $this->site, + $this->site->getDefaultLanguage(), + ), + ]; self::assertEquals($expected, $this->subject->get($this->site)); } diff --git a/Tests/Unit/Sitemap/Provider/SiteProviderTest.php b/Tests/Unit/Sitemap/Provider/SiteProviderTest.php index c82b9642e..49628a976 100644 --- a/Tests/Unit/Sitemap/Provider/SiteProviderTest.php +++ b/Tests/Unit/Sitemap/Provider/SiteProviderTest.php @@ -25,7 +25,6 @@ use EliasHaeussler\Typo3Warming as Src; use Generator; -use GuzzleHttp\Psr7\Uri; use PHPUnit\Framework; use TYPO3\CMS\Core; use TYPO3\TestingFramework; @@ -48,11 +47,11 @@ protected function setUp(): void } #[Framework\Attributes\Test] - public function getReturnsNullIfSitemapPathIsNotConfiguredInSite(): void + public function getReturnsEmptyArrayIfSitemapPathIsNotConfiguredInSite(): void { $site = new Core\Site\Entity\Site('foo', 1, []); - self::assertNull($this->subject->get($site)); + self::assertSame([], $this->subject->get($site)); } #[Framework\Attributes\Test] @@ -63,11 +62,15 @@ public function getReturnsSitemapWithUrlPathFromSite(string $path, string $expec 'base' => 'https://www.example.com/', 'xml_sitemap_path' => $path, ]); + $sitemaps = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri($expected), + $site, + $site->getDefaultLanguage(), + ), + ]; - self::assertEquals( - new Src\Sitemap\SiteAwareSitemap(new Core\Http\Uri($expected), $site, $site->getDefaultLanguage()), - $this->subject->get($site), - ); + self::assertEquals($sitemaps, $this->subject->get($site)); } #[Framework\Attributes\Test] @@ -80,16 +83,20 @@ public function getReturnsSitemapWithUrlPathFromSiteLanguage(string $path, strin $siteLanguage = new Core\Site\Entity\SiteLanguage( 1, 'de_DE.UTF-8', - new Uri('https://www.example.com/de'), + new Core\Http\Uri('https://www.example.com/de'), [ 'xml_sitemap_path' => $path, ], ); + $sitemaps = [ + new Src\Sitemap\SiteAwareSitemap( + new Core\Http\Uri($expected), + $site, + $siteLanguage, + ), + ]; - self::assertEquals( - new Src\Sitemap\SiteAwareSitemap(new Uri($expected), $site, $siteLanguage), - $this->subject->get($site, $siteLanguage), - ); + self::assertEquals($sitemaps, $this->subject->get($site, $siteLanguage)); } /**