diff --git a/core/lib/Drupal/Core/Routing/CachedUrlGenerator.php b/core/lib/Drupal/Core/Routing/CachedUrlGenerator.php index 4ba98cb..a84e81c 100644 --- a/core/lib/Drupal/Core/Routing/CachedUrlGenerator.php +++ b/core/lib/Drupal/Core/Routing/CachedUrlGenerator.php @@ -38,7 +38,9 @@ class CachedUrlGenerator implements DestructableInterface, CachedUrlGeneratorInt protected $cache; /** - * An array of cached URLs keyed by route name or path. + * A nested array of cached URLs keyed by request URI. + * + * Each nested array contains URLs cached by route name or path. * * @var array */ @@ -59,11 +61,20 @@ class CachedUrlGenerator implements DestructableInterface, CachedUrlGeneratorInt protected $cacheNeedsWriting = FALSE; /** - * The cache key to use when caching generated URLs. + * The cache keys to use when caching generated URLs. + * + * An array of request URIs. + * + * @var array + */ + protected $cacheKeys; + + /** + * The request stack service. * - * @var string + * @var \Symfony\Component\HttpFoundation\RequestStack */ - protected $cacheKey; + protected $requestStack; /** * Cache prefix for route names. @@ -91,15 +102,18 @@ public function __construct(UrlGeneratorInterface $url_generator, CacheBackendIn $this->urlGenerator = $url_generator; $this->cache = $cache; $this->routeProvider = $route_provider; + $this->requestStack = $request_stack; // Select cache based on the current request URI. We store one entry for // each URI. - $this->cacheKey = $request_stack->getCurrentRequest()->getUri(); + $this->cacheKeys[] = $request_stack->getCurrentRequest()->getUri(); // Retrieve stored URLs. - $cached = $this->cache->get($this->cacheKey); - if ($cached) { - $this->cachedUrls = $cached->data; + foreach ($this->cacheKeys as $cache_key) { + $cached = $this->cache->get($cache_key); + if ($cached) { + $this->cachedUrls[$cache_key] = $cached->data; + } } } @@ -108,17 +122,21 @@ public function __construct(UrlGeneratorInterface $url_generator, CacheBackendIn */ public function clearCache() { $this->cachedUrls = array(); - $this->cache->delete($this->cacheKey); + foreach ($this->cacheKeys as $cache_key) { + $this->cache->delete($cache_key); + } } /** * Writes the cache of generated URLs. */ protected function writeCache() { - if ($this->cacheNeedsWriting && !empty($this->cachedUrls) && !empty($this->cacheKey)) { - // Set the URL cache to expire in 24 hours. - $expire = REQUEST_TIME + (60 * 60 * 24); - $this->cache->set($this->cacheKey, $this->cachedUrls, $expire); + if ($this->cacheNeedsWriting && !empty($this->cachedUrls) && !empty($this->cacheKeys)) { + foreach ($this->cacheKeys as $cache_key) { + // Set the URL cache to expire in 24 hours. + $expire = REQUEST_TIME + (60 * 60 * 24); + $this->cache->set($this->cacheKeys, $this->cachedUrls[$cache_key], $expire); + } } } @@ -140,11 +158,12 @@ public function generate($name, $parameters = array(), $absolute = FALSE) { */ public function generateFromPath($path = NULL, $options = array()) { $key = static::PATH_CACHE_PREFIX . hash('sha256', $path . serialize($options)); - if (!isset($this->cachedUrls[$key])) { - $this->cachedUrls[$key] = $this->urlGenerator->generateFromPath($path, $options); + $cache_key = $this->requestStack->getCurrentRequest()->getUri(); + if (!isset($this->cachedUrls[$cache_key][$key])) { + $this->cachedUrls[$cache_key][$key] = $this->urlGenerator->generateFromPath($path, $options); $this->cacheNeedsWriting = TRUE; } - return $this->cachedUrls[$key]; + return $this->cachedUrls[$cache_key][$key]; } /** @@ -153,18 +172,19 @@ public function generateFromPath($path = NULL, $options = array()) { public function generateFromRoute($name, $parameters = array(), $options = array()) { // In some cases $name may be a Route object, rather than a string. $key = static::ROUTE_CACHE_PREFIX . hash('sha256', serialize($name) . serialize($options) . serialize($parameters)); - if (!isset($this->cachedUrls[$key])) { + $cache_key = $this->requestStack->getCurrentRequest()->getUri(); + if (!isset($this->cachedUrls[$cache_key][$key])) { $generated_url = $this->urlGenerator->generateFromRoute($name, $parameters, $options); $route = $this->getRoute($name); if ($route->hasOption('_cacheable') && !$route->getOption('_cacheable')) { return $generated_url; } else { - $this->cachedUrls[$key] = $generated_url; + $this->cachedUrls[$cache_key][$key] = $generated_url; $this->cacheNeedsWriting = TRUE; } } - return $this->cachedUrls[$key]; + return $this->cachedUrls[$cache_key][$key]; } /** diff --git a/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorTest.php b/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorTest.php index 6242bb6..14fafce 100644 --- a/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorTest.php @@ -155,13 +155,14 @@ public function testGenerateFromPath() { * @see \Drupal\Core\Routing\CachedUrlGenerator::generateFromPath() */ public function testGenerateFromPathWithDifferentParameters() { - // We are generating URL's four times but since two of them are cached, - // we expect that generateFromPath will only be called twice. - $this->urlGenerator->expects($this->exactly(2)) + // We are generating URL's six times but since three of them are cached, + // we expect that generateFromPath will only be called three times. + $this->urlGenerator->expects($this->exactly(3)) ->method('generateFromPath') ->will($this->returnValueMap(array( array('test-route-1', array('absolute' => TRUE), 'http://localhost/test-route-1'), array('test-route-1', array('absolute' => FALSE), 'test-route-1'), + array('test-route-1', array('absolute' => TRUE), 'http://localhost/test-route-1'), ))); // First call will prime the cache. @@ -174,6 +175,12 @@ public function testGenerateFromPathWithDifferentParameters() { $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generateFromPath('test-route-1', array('absolute' => FALSE))); // Fourth call will fetch from cache. $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generateFromPath('test-route-1', array('absolute' => FALSE))); + $new_request = Request::create('/foo'); + $this->requestStack->push($new_request); + // Fifth call uses the first path again but a new request. + $this->assertEquals('http://localhost/test-route-1', $this->cachedUrlGenerator->generateFromPath('test-route-1', array('absolute' => TRUE))); + // Sixth call will fetch from cache. + $this->assertEquals('http://localhost/test-route-1', $this->cachedUrlGenerator->generateFromPath('test-route-1', array('absolute' => TRUE))); }