Created
November 12, 2025 14:49
-
-
Save junaidpv/19163c62a6328a2dc82db3718d9a275d to your computer and use it in GitHub Desktop.
Patch form of the change form https://www.drupal.org/project/domain/issues/3555807#comment-16334891 (https://git.drupalcode.org/project/domain/-/commit/ce0db498a3ea38ef13d7cf01561d9a5cbea557dd)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/domain_config/src/DomainConfigOverrider.php b/domain_config/src/DomainConfigOverrider.php | |
| index 0ff3bcb0f13a7d615e6d6c6b7cb32c9f42c211ad..51cbbd6b94eced2ae8f6058ccae2cf8abda4d693 100644 | |
| --- a/domain_config/src/DomainConfigOverrider.php | |
| +++ b/domain_config/src/DomainConfigOverrider.php | |
| @@ -28,6 +28,19 @@ class DomainConfigOverrider implements ConfigFactoryOverrideInterface { | |
| */ | |
| protected const DOMAIN_CONFIG_PREFIX = 'domain.config.'; | |
| + /** | |
| + * Cache of previously looked up configuration overrides. | |
| + * | |
| + * Stores configuration override results keyed by a hash of config names | |
| + * and language ID to prevent repeating expensive lookup operations. | |
| + * The array format is: | |
| + * - key: MD5 hash of imploded config names concatenated with language ID | |
| + * - value: Array of configuration overrides for those names. | |
| + * | |
| + * @var array | |
| + */ | |
| + protected static $overridesCache = []; | |
| + | |
| /** | |
| * The domain negotiator. | |
| * | |
| @@ -111,6 +124,13 @@ class DomainConfigOverrider implements ConfigFactoryOverrideInterface { | |
| public function __construct(StorageInterface $storage, ModuleHandlerInterface $module_handler) { | |
| $this->storage = $storage; | |
| $this->moduleHandler = $module_handler; | |
| + $this->initialize(); | |
| + } | |
| + | |
| + /** | |
| + * Initializes the domain configuration overrider state. | |
| + */ | |
| + protected function initialize() { | |
| // Check if domain configs are available and if there are any overrides | |
| // in settings.php. If not, we can skip the overrides. | |
| // See https://www.drupal.org/project/domain/issues/3126532. | |
| @@ -134,6 +154,24 @@ class DomainConfigOverrider implements ConfigFactoryOverrideInterface { | |
| } | |
| } | |
| + /** | |
| + * Reset the override cache and context. | |
| + * | |
| + * Used mainly for testing. | |
| + */ | |
| + public function reset() { | |
| + // Reset the cache. | |
| + static::$overridesCache = []; | |
| + // Check for configuration overrides updates. | |
| + $this->initialize(); | |
| + if ($this->hasOverrides) { | |
| + // Reset the context. | |
| + $this->domain = NULL; | |
| + $this->contextSet = NULL; | |
| + $this->cacheSuffix = NULL; | |
| + } | |
| + } | |
| + | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| @@ -148,12 +186,11 @@ class DomainConfigOverrider implements ConfigFactoryOverrideInterface { | |
| } | |
| else { | |
| // Try to prevent repeating lookups. | |
| - static $lookups; | |
| // Key should be a known length, so hash. | |
| // We add the language as a suffix as it can change after negotiation. | |
| $key = md5(implode(':', $names) . ':' . $this->language->getId()); | |
| - if (isset($lookups[$key])) { | |
| - return $lookups[$key]; | |
| + if (isset(static::$overridesCache[$key])) { | |
| + return static::$overridesCache[$key]; | |
| } | |
| // Prepare our overrides. | |
| @@ -163,7 +200,7 @@ class DomainConfigOverrider implements ConfigFactoryOverrideInterface { | |
| // So ensure that we are _not_ looking up a domain.record.*. | |
| // We also skip overriding the domain.settings config. | |
| if ($this->isInternalName(current($names))) { | |
| - $lookups[$key] = $overrides; | |
| + static::$overridesCache[$key] = $overrides; | |
| return $overrides; | |
| } | |
| @@ -212,12 +249,12 @@ class DomainConfigOverrider implements ConfigFactoryOverrideInterface { | |
| } | |
| } | |
| } | |
| - $lookups[$key] = $overrides; | |
| + static::$overridesCache[$key] = $overrides; | |
| } | |
| else { | |
| if ($this->domain === FALSE) { | |
| // No domain exists, so we can safely cache the empty overrides. | |
| - $lookups[$key] = $overrides; | |
| + static::$overridesCache[$key] = $overrides; | |
| } | |
| } | |
| diff --git a/domain_source/src/HttpKernel/DomainSourceRouteMatcher.php b/domain_source/src/HttpKernel/DomainSourceRouteMatcher.php | |
| index 491b4805bb8bec450ddc9c74996987c95f0454e1..dbe15f882eb589d370acab0b9e6bf81f89bb1007 100644 | |
| --- a/domain_source/src/HttpKernel/DomainSourceRouteMatcher.php | |
| +++ b/domain_source/src/HttpKernel/DomainSourceRouteMatcher.php | |
| @@ -2,6 +2,7 @@ | |
| namespace Drupal\domain_source\HttpKernel; | |
| +use Drupal\Core\Routing\CacheableRouteProviderInterface; | |
| use Drupal\Core\Routing\RequestContext; | |
| use Drupal\Core\Routing\RouteObjectInterface; | |
| use Symfony\Component\ExpressionLanguage\ExpressionLanguage; | |
| @@ -52,6 +53,10 @@ class DomainSourceRouteMatcher { | |
| protected static function getRouteProvider() { | |
| if (!isset(static::$routeProvider)) { | |
| static::$routeProvider = \Drupal::service('domain_source.route_provider'); | |
| + if (static::$routeProvider instanceof CacheableRouteProviderInterface) { | |
| + $domain_id = \Drupal::service('domain.negotiator')->getActiveId(); | |
| + static::$routeProvider->addExtraCacheKeyPart('domain', $domain_id); | |
| + } | |
| } | |
| return static::$routeProvider; | |
| } | |
| diff --git a/domain_source/tests/src/Functional/DomainSourceRouteMatcherTest.php b/domain_source/tests/src/Functional/DomainSourceRouteMatcherTest.php | |
| new file mode 100644 | |
| index 0000000000000000000000000000000000000000..7d51f81ab589d005e5a9f3413fe3aa6372c1bc13 | |
| --- /dev/null | |
| +++ b/domain_source/tests/src/Functional/DomainSourceRouteMatcherTest.php | |
| @@ -0,0 +1,138 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\Tests\domain_source\Functional; | |
| + | |
| +use Drupal\domain_source\HttpKernel\DomainSourceRouteMatcher; | |
| +use Drupal\Tests\domain\Functional\DomainTestBase; | |
| + | |
| +/** | |
| + * Tests that route match results are correctly cached per domain. | |
| + * | |
| + * Verifies that when different domains have different home page | |
| + * configurations via system.site, the routeMatch() method returns | |
| + * different cached results for each domain. | |
| + * | |
| + * @group domain_source | |
| + */ | |
| +class DomainSourceRouteMatcherTest extends DomainTestBase { | |
| + | |
| + /** | |
| + * Disabled config schema checking for domain config. | |
| + * | |
| + * @var bool | |
| + */ | |
| + protected $strictConfigSchema = FALSE; | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + protected static $modules = [ | |
| + 'domain', | |
| + 'domain_source', | |
| + 'domain_config', | |
| + 'domain_config_test', | |
| + 'node', | |
| + 'system', | |
| + 'user', | |
| + ]; | |
| + | |
| + /** | |
| + * Test nodes for different home pages. | |
| + * | |
| + * @var \Drupal\node\NodeInterface[] | |
| + */ | |
| + protected $testNodes; | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + protected function setUp(): void { | |
| + parent::setUp(); | |
| + | |
| + // Create test domains. This will create domains with IDs like | |
| + // example_com, one_example_com, etc. | |
| + // The domain_config_test module provides pre-configured domain configs. | |
| + $this->domainCreateTestDomains(5); | |
| + | |
| + // Create test nodes that match the domain_config_test expectations. | |
| + $this->testNodes[0] = $this->drupalCreateNode([ | |
| + 'type' => 'page', | |
| + 'title' => 'Node 1', | |
| + 'status' => 1, | |
| + ]); | |
| + $this->testNodes[1] = $this->drupalCreateNode([ | |
| + 'type' => 'page', | |
| + 'title' => 'Node 2', | |
| + 'status' => 1, | |
| + ]); | |
| + } | |
| + | |
| + /** | |
| + * Tests that routeMatch returns different cached results per domain. | |
| + */ | |
| + public function testRouteMatchReturnsDifferentResultsPerDomain() { | |
| + // Load specific domains that have pre-configured home pages. | |
| + // one_example_com has /node/1 configured as front page. | |
| + // example_com has the default /node configured. | |
| + $domain_storage = \Drupal::entityTypeManager()->getStorage('domain'); | |
| + $domain_one = $domain_storage->load('one_example_com'); | |
| + $domain_default = $domain_storage->load('example_com'); | |
| + | |
| + $this->assertNotNull($domain_one, 'Domain one_example_com should exist.'); | |
| + $this->assertNotNull($domain_default, 'Domain example_com should exist.'); | |
| + | |
| + // Set one_example_com as active (has /node/1 as front page). | |
| + $negotiator = \Drupal::service('domain.negotiator'); | |
| + $negotiator->setActiveDomain($domain_one); | |
| + // Reset the domain config overrider to ensure a clean state. | |
| + \Drupal::service('domain_config.overrider')->reset(); | |
| + // Reset the static route provider to ensure a clean state. | |
| + $this->resetRouteProviderStatic(); | |
| + | |
| + // Call routeMatch for the home page with domain_one. | |
| + $match1 = DomainSourceRouteMatcher::routeMatch('/'); | |
| + | |
| + // Verify we got a match for domain_one that contains route information. | |
| + $this->assertIsArray($match1, 'Route match should return an array.'); | |
| + $this->assertArrayHasKey('_route', $match1, 'Route match should contain _route.'); | |
| + | |
| + // Domain_one should route to entity.node.canonical (because /node/1). | |
| + $route1_name = $match1['_route']; | |
| + $this->assertEquals('entity.node.canonical', $route1_name, | |
| + 'Domain one_example_com should route to node page.'); | |
| + | |
| + // Now switch to example_com (has /node as front page). | |
| + $negotiator->setActiveDomain($domain_default); | |
| + // Reset the domain config overrider to ensure a clean state. | |
| + \Drupal::service('domain_config.overrider')->reset(); | |
| + // Reset the static route provider to force re-initialization. | |
| + $this->resetRouteProviderStatic(); | |
| + | |
| + // Call routeMatch for the home page with domain_default. | |
| + $match2 = DomainSourceRouteMatcher::routeMatch('/'); | |
| + | |
| + // Verify we got a match for domain_default. | |
| + $this->assertIsArray($match2, 'Route match should return an array.'); | |
| + $this->assertArrayHasKey('_route', $match2, 'Route match should contain _route.'); | |
| + | |
| + // Domain_default routes are different - it goes to system.404 or | |
| + // a listing page, not a specific node. | |
| + $route2_name = $match2['_route']; | |
| + | |
| + // CRITICAL: Verify the routes are actually different. | |
| + // This proves that the cache is per-domain. | |
| + $this->assertNotEquals($route1_name, $route2_name, | |
| + 'Different domains MUST return different cached routes.'); | |
| + } | |
| + | |
| + /** | |
| + * Resets the static routeProvider property in DomainSourceRouteMatcher. | |
| + */ | |
| + protected function resetRouteProviderStatic(): void { | |
| + $reflection = new \ReflectionClass(DomainSourceRouteMatcher::class); | |
| + $property = $reflection->getProperty('routeProvider'); | |
| + $property->setAccessible(TRUE); | |
| + $property->setValue(NULL, NULL); | |
| + } | |
| + | |
| +} | |
| diff --git a/domain_source/tests/src/Functional/DomainSourceRouterProviderTest.php b/domain_source/tests/src/Functional/DomainSourceRouterProviderTest.php | |
| new file mode 100644 | |
| index 0000000000000000000000000000000000000000..9a303f08541ee01deb43e6c72e40215a063e956e | |
| --- /dev/null | |
| +++ b/domain_source/tests/src/Functional/DomainSourceRouterProviderTest.php | |
| @@ -0,0 +1,168 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\Tests\domain_source\Functional; | |
| + | |
| +use Drupal\domain_source\HttpKernel\DomainSourceRouteMatcher; | |
| +use Drupal\Tests\domain\Functional\DomainTestBase; | |
| +use Symfony\Component\HttpFoundation\Request; | |
| + | |
| +/** | |
| + * Tests that route cache IDs include the domain ID. | |
| + * | |
| + * This test verifies the complete integration: when | |
| + * DomainSourceRouteMatcher::getRouteProvider() injects the domain ID via | |
| + * addExtraCacheKeyPart(), the cache ID returned by | |
| + * DomainSourceRouteProvider::getRouteCollectionCacheId() includes it. | |
| + * | |
| + * @group domain_source | |
| + */ | |
| +class DomainSourceRouterProviderTest extends DomainTestBase { | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + protected static $modules = [ | |
| + 'domain', | |
| + 'domain_source', | |
| + 'node', | |
| + ]; | |
| + | |
| + /** | |
| + * The domain source route provider service. | |
| + * | |
| + * @var \Drupal\domain_source\HttpKernel\DomainSourceRouteProvider | |
| + */ | |
| + protected $routeProvider; | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + protected function setUp(): void { | |
| + parent::setUp(); | |
| + | |
| + // Create two test domains. | |
| + $this->domainCreateTestDomains(2); | |
| + | |
| + // Get the route provider service. | |
| + $this->routeProvider = $this->container->get('domain_source.route_provider'); | |
| + } | |
| + | |
| + /** | |
| + * Tests that cache ID includes the domain ID. | |
| + */ | |
| + public function testCacheIdIncludesDomainId() { | |
| + // Load the domains. | |
| + $domains = \Drupal::entityTypeManager() | |
| + ->getStorage('domain') | |
| + ->loadMultiple(); | |
| + $domain_values = array_values($domains); | |
| + $domain1 = $domain_values[0]; | |
| + $domain2 = $domain_values[1]; | |
| + | |
| + // Create a request. | |
| + $request = Request::create('/node'); | |
| + | |
| + // Set the first domain as active. | |
| + \Drupal::service('domain.negotiator')->setActiveDomain($domain1); | |
| + | |
| + // Reset and reinitialize for the first domain. | |
| + $this->resetRouteProviderStatic(); | |
| + | |
| + // Get the cache ID. | |
| + $cache_id1 = $this->getRouteCollectionCacheId($request); | |
| + | |
| + // Verify that the cache ID contains the domain identifier. | |
| + $this->assertStringContainsString( | |
| + '[domain]=' . $domain1->id(), | |
| + $cache_id1, | |
| + 'Cache ID should contain the first domain ID.' | |
| + ); | |
| + | |
| + // Now switch to the second domain. | |
| + \Drupal::service('domain.negotiator')->setActiveDomain($domain2); | |
| + | |
| + // Reset and reinitialize for the second domain. | |
| + $this->resetRouteProviderStatic(); | |
| + | |
| + // Get the cache ID for the second domain. | |
| + $cache_id2 = $this->getRouteCollectionCacheId($request); | |
| + | |
| + // Verify that the cache ID contains the second domain identifier. | |
| + $this->assertStringContainsString( | |
| + '[domain]=' . $domain2->id(), | |
| + $cache_id2, | |
| + 'Cache ID should contain the second domain ID.' | |
| + ); | |
| + } | |
| + | |
| + /** | |
| + * Tests the cache ID format. | |
| + */ | |
| + public function testCacheIdFormat() { | |
| + // Load a domain. | |
| + $domains = \Drupal::entityTypeManager() | |
| + ->getStorage('domain') | |
| + ->loadMultiple(); | |
| + $domain = reset($domains); | |
| + | |
| + // Set the domain as active. | |
| + \Drupal::service('domain.negotiator')->setActiveDomain($domain); | |
| + | |
| + // Reset and initialize. | |
| + $this->resetRouteProviderStatic(); | |
| + | |
| + // Create a request. | |
| + $request = Request::create('/node'); | |
| + | |
| + // Get the cache ID. | |
| + $cache_id = $this->getRouteCollectionCacheId($request); | |
| + | |
| + // Verify the format: route:[key1]=val1:[key2]=val2:...:/path. | |
| + $this->assertMatchesRegularExpression( | |
| + '/^route:.*\[domain\]=' . preg_quote($domain->id(), '/') . '.*:\/node$/', | |
| + $cache_id, | |
| + 'Cache ID should have the correct format with domain information.' | |
| + ); | |
| + } | |
| + | |
| + /** | |
| + * Resets the static routeProvider property in DomainSourceRouteMatcher. | |
| + * | |
| + * This method uses reflection to reset the static route provider property | |
| + * to NULL, forcing re-initialization of the route provider. This is necessary | |
| + * to ensure that domain-specific cache key parts are properly injected when | |
| + * switching between different active domains during testing. | |
| + */ | |
| + protected function resetRouteProviderStatic(): void { | |
| + // Reset the static route provider to force re-initialization. | |
| + $reflection = new \ReflectionClass(DomainSourceRouteMatcher::class); | |
| + $property = $reflection->getProperty('routeProvider'); | |
| + $property->setAccessible(TRUE); | |
| + $property->setValue(NULL, NULL); | |
| + // Call getRouteProvider() which injects the domain ID. | |
| + $method = $reflection->getMethod('getRouteProvider'); | |
| + $method->setAccessible(TRUE); | |
| + $method->invoke(NULL); | |
| + } | |
| + | |
| + /** | |
| + * Gets the route collection cache ID from the route provider. | |
| + * | |
| + * This helper method uses reflection to access the protected | |
| + * getRouteCollectionCacheId method from the parent RouteProvider class. | |
| + * | |
| + * @param \Symfony\Component\HttpFoundation\Request $request | |
| + * The HTTP request. | |
| + * | |
| + * @return string | |
| + * The cache ID for the route collection. | |
| + */ | |
| + protected function getRouteCollectionCacheId(Request $request): string { | |
| + // Get the cache ID for the second domain. | |
| + $reflection = new \ReflectionClass($this->routeProvider); | |
| + $get_cache_id_method = $reflection->getMethod('getRouteCollectionCacheId'); | |
| + $get_cache_id_method->setAccessible(TRUE); | |
| + return $get_cache_id_method->invoke($this->routeProvider, $request); | |
| + } | |
| + | |
| +} | |
| diff --git a/domain_source/tests/src/Unit/DomainSourceRouterProviderTest.php b/domain_source/tests/src/Unit/DomainSourceRouterProviderTest.php | |
| new file mode 100644 | |
| index 0000000000000000000000000000000000000000..944ad2a9ae2c6f1eebe93b72202ade06f2150896 | |
| --- /dev/null | |
| +++ b/domain_source/tests/src/Unit/DomainSourceRouterProviderTest.php | |
| @@ -0,0 +1,233 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\Tests\domain_source\Unit; | |
| + | |
| +use Drupal\Core\Cache\CacheBackendInterface; | |
| +use Drupal\Core\Language\LanguageInterface; | |
| +use Drupal\Core\Language\LanguageManagerInterface; | |
| +use Drupal\Core\Path\CurrentPathStack; | |
| +use Drupal\Core\PathProcessor\PathProcessorManager; | |
| +use Drupal\Core\Cache\CacheTagsInvalidatorInterface; | |
| +use Drupal\Core\Database\Connection; | |
| +use Drupal\Core\State\StateInterface; | |
| +use Drupal\domain\DomainNegotiatorInterface; | |
| +use Drupal\domain_source\HttpKernel\DomainSourceRouteProvider; | |
| +use Drupal\domain_source\HttpKernel\DomainSourceRouteMatcher; | |
| +use Drupal\Tests\UnitTestCase; | |
| +use Symfony\Component\DependencyInjection\ContainerInterface; | |
| +use Symfony\Component\HttpFoundation\Request; | |
| + | |
| +/** | |
| + * Tests that cache IDs include the domain ID. | |
| + * | |
| + * Verifies that when DomainSourceRouteMatcher::getRouteProvider() injects | |
| + * the domain ID via addExtraCacheKeyPart(), the cache ID returned by | |
| + * DomainSourceRouteProvider::getRouteCollectionCacheId() includes it. | |
| + * | |
| + * @coversDefaultClass \Drupal\domain_source\HttpKernel\DomainSourceRouteProvider | |
| + * @group domain_source | |
| + */ | |
| +class DomainSourceRouterProviderTest extends UnitTestCase { | |
| + | |
| + /** | |
| + * The domain source route provider. | |
| + * | |
| + * @var \Drupal\domain_source\HttpKernel\DomainSourceRouteProvider | |
| + */ | |
| + protected $routeProvider; | |
| + | |
| + /** | |
| + * The mocked domain negotiator. | |
| + * | |
| + * @var \Drupal\domain\DomainNegotiatorInterface|\PHPUnit\Framework\MockObject\MockObject | |
| + */ | |
| + protected $domainNegotiator; | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + protected function setUp(): void { | |
| + parent::setUp(); | |
| + | |
| + // Mock all the dependencies required by RouteProvider. | |
| + $database = $this->createMock(Connection::class); | |
| + $state = $this->createMock(StateInterface::class); | |
| + $current_path = $this->createMock(CurrentPathStack::class); | |
| + $cache = $this->createMock(CacheBackendInterface::class); | |
| + $path_processor = $this->createMock(PathProcessorManager::class); | |
| + $cache_tags_invalidator = $this->createMock(CacheTagsInvalidatorInterface::class); | |
| + | |
| + // Mock language manager to return a language. | |
| + $language = $this->createMock(LanguageInterface::class); | |
| + $language->method('getId')->willReturn('en'); | |
| + | |
| + $language_manager = $this->createMock(LanguageManagerInterface::class); | |
| + $language_manager->method('getCurrentLanguage')->willReturn($language); | |
| + | |
| + // Configure path processor to return the path as-is. | |
| + $path_processor->method('processInbound') | |
| + ->willReturnCallback(function ($path) { | |
| + return $path; | |
| + }); | |
| + | |
| + // Create the actual DomainSourceRouteProvider with all dependencies. | |
| + $this->routeProvider = new DomainSourceRouteProvider( | |
| + $database, | |
| + $state, | |
| + $current_path, | |
| + $cache, | |
| + $path_processor, | |
| + $cache_tags_invalidator, | |
| + 'router', | |
| + $language_manager | |
| + ); | |
| + | |
| + // Mock domain negotiator. | |
| + $this->domainNegotiator = $this->createMock(DomainNegotiatorInterface::class); | |
| + | |
| + // Set up the container. | |
| + $container = $this->createMock(ContainerInterface::class); | |
| + $container->method('get') | |
| + ->willReturnMap([ | |
| + ['domain.negotiator', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->domainNegotiator], | |
| + ['domain_source.route_provider', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->routeProvider], | |
| + ]); | |
| + | |
| + \Drupal::setContainer($container); | |
| + } | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + protected function tearDown(): void { | |
| + parent::tearDown(); | |
| + | |
| + // Reset the static properties. | |
| + $reflection = new \ReflectionClass(DomainSourceRouteMatcher::class); | |
| + $property = $reflection->getProperty('routeProvider'); | |
| + $property->setAccessible(TRUE); | |
| + $property->setValue(NULL, NULL); | |
| + } | |
| + | |
| + /** | |
| + * Tests that cache ID includes the domain ID. | |
| + * | |
| + * @covers ::getRouteCollectionCacheId | |
| + */ | |
| + public function testCacheIdIncludesDomainId() { | |
| + $domain_id = 'example_com'; | |
| + | |
| + // Configure the domain negotiator to return a specific domain ID. | |
| + $this->domainNegotiator->method('getActiveId')->willReturn($domain_id); | |
| + | |
| + // Reset the static route provider. | |
| + $reflection = new \ReflectionClass(DomainSourceRouteMatcher::class); | |
| + $property = $reflection->getProperty('routeProvider'); | |
| + $property->setAccessible(TRUE); | |
| + $property->setValue(NULL, NULL); | |
| + | |
| + // Call DomainSourceRouteMatcher::getRouteProvider() | |
| + // which injects the domain. | |
| + $method = $reflection->getMethod('getRouteProvider'); | |
| + $method->setAccessible(TRUE); | |
| + $method->invoke(NULL); | |
| + | |
| + // Create a request. | |
| + $request = Request::create('/test-path'); | |
| + | |
| + // Get the cache ID using reflection. | |
| + $reflection = new \ReflectionClass($this->routeProvider); | |
| + $get_cache_id_method = $reflection->getMethod('getRouteCollectionCacheId'); | |
| + $get_cache_id_method->setAccessible(TRUE); | |
| + $cache_id = $get_cache_id_method->invoke($this->routeProvider, $request); | |
| + | |
| + // Verify that the cache ID contains the domain identifier. | |
| + $this->assertStringContainsString('[domain]=' . $domain_id, $cache_id, 'Cache ID should contain the domain ID.'); | |
| + $this->assertStringContainsString('/test-path', $cache_id, 'Cache ID should contain the path.'); | |
| + } | |
| + | |
| + /** | |
| + * Tests that different domains produce different cache IDs. | |
| + * | |
| + * @covers ::getRouteCollectionCacheId | |
| + */ | |
| + public function testDifferentDomainsProduceDifferentCacheIds() { | |
| + $domain_id1 = 'example_com'; | |
| + $domain_id2 = 'test_com'; | |
| + | |
| + // Configure for first domain. | |
| + $this->domainNegotiator->method('getActiveId')->willReturn($domain_id1); | |
| + | |
| + // Reset and call getRouteProvider for first domain. | |
| + $this->resetRouteProviderStatic(); | |
| + $this->callGetRouteProvider(); | |
| + | |
| + // Get cache ID for first domain. | |
| + $request = Request::create('/test-path'); | |
| + $cache_id1 = $this->getRouteCollectionCacheId($request); | |
| + | |
| + // Now configure for second domain. | |
| + $this->domainNegotiator = $this->createMock(DomainNegotiatorInterface::class); | |
| + $this->domainNegotiator->method('getActiveId')->willReturn($domain_id2); | |
| + | |
| + // Update container with new negotiator. | |
| + $container = $this->createMock(ContainerInterface::class); | |
| + $container->method('get') | |
| + ->willReturnMap([ | |
| + ['domain.negotiator', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->domainNegotiator], | |
| + ['domain_source.route_provider', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->routeProvider], | |
| + ]); | |
| + \Drupal::setContainer($container); | |
| + | |
| + // Reset and call getRouteProvider for second domain. | |
| + $this->resetRouteProviderStatic(); | |
| + $this->callGetRouteProvider(); | |
| + | |
| + // Get cache ID for second domain. | |
| + $cache_id2 = $this->getRouteCollectionCacheId($request); | |
| + | |
| + // Verify both cache IDs contain their respective domain IDs. | |
| + $this->assertStringContainsString('[domain]=' . $domain_id1, $cache_id1); | |
| + $this->assertStringContainsString('[domain]=' . $domain_id2, $cache_id2); | |
| + | |
| + // Verify the cache IDs are different. | |
| + $this->assertNotEquals($cache_id1, $cache_id2, 'Cache IDs should be different for different domains.'); | |
| + } | |
| + | |
| + /** | |
| + * Helper method to reset the static route provider property. | |
| + */ | |
| + protected function resetRouteProviderStatic(): void { | |
| + $reflection = new \ReflectionClass(DomainSourceRouteMatcher::class); | |
| + $property = $reflection->getProperty('routeProvider'); | |
| + $property->setAccessible(TRUE); | |
| + $property->setValue(NULL, NULL); | |
| + } | |
| + | |
| + /** | |
| + * Helper method to call getRouteProvider. | |
| + */ | |
| + protected function callGetRouteProvider(): void { | |
| + $reflection = new \ReflectionClass(DomainSourceRouteMatcher::class); | |
| + $method = $reflection->getMethod('getRouteProvider'); | |
| + $method->setAccessible(TRUE); | |
| + $method->invoke(NULL); | |
| + } | |
| + | |
| + /** | |
| + * Helper method to get the route collection cache ID. | |
| + * | |
| + * @param \Symfony\Component\HttpFoundation\Request $request | |
| + * The request. | |
| + * | |
| + * @return string | |
| + * The cache ID. | |
| + */ | |
| + protected function getRouteCollectionCacheId(Request $request): string { | |
| + $reflection = new \ReflectionClass($this->routeProvider); | |
| + $method = $reflection->getMethod('getRouteCollectionCacheId'); | |
| + $method->setAccessible(TRUE); | |
| + return $method->invoke($this->routeProvider, $request); | |
| + } | |
| + | |
| +} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment