diff --git a/core/core.services.yml b/core/core.services.yml index 3223c72..0f67730 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -246,7 +246,7 @@ services: arguments: ['@router.route_provider', '@path_processor_manager', '@config.factory', '@settings'] url_generator: class: Drupal\Core\Routing\CachedUrlGenerator - arguments: ['@url_generator.uncached', '@cache.url_generator'] + arguments: ['@url_generator.uncached', '@cache.url_generator', '@language_manager'] calls: - [setRequest, ['@?request']] - [setContext, ['@?router.request_context']] diff --git a/core/lib/Drupal/Core/Routing/CachedUrlGenerator.php b/core/lib/Drupal/Core/Routing/CachedUrlGenerator.php index d1acdb2..0625a05 100644 --- a/core/lib/Drupal/Core/Routing/CachedUrlGenerator.php +++ b/core/lib/Drupal/Core/Routing/CachedUrlGenerator.php @@ -9,16 +9,17 @@ use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\DestructableInterface; +use Drupal\Core\Language\LanguageManager; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\RequestContext; /** - * Class used by the PathSubscriber to get the system path and cache path lookups. + * Class used to wrap a UrlGenerator to provide caching of the generated values. */ class CachedUrlGenerator implements DestructableInterface, UrlGeneratorInterface { /** - * The wrapped url generator + * The wrapped URL generator * * @var \Drupal\Core\Routing\UrlGeneratorInterface */ @@ -32,6 +33,13 @@ class CachedUrlGenerator implements DestructableInterface, UrlGeneratorInterface protected $cache; /** + * Language manager for retrieving the URL language type. + * + * @var \Drupal\Core\Language\LanguageManager + */ + protected $languageManager; + + /** * An array of cached URLs keyed by route name or path. * * @var array @@ -63,20 +71,23 @@ class CachedUrlGenerator implements DestructableInterface, UrlGeneratorInterface const PATH_CACHE_PREFIX = 'path::'; /** - * Constructs a \Drupal\Core\CacheDecorator\AliasManagerCacheDecorator. + * Constructs a \Drupal\Core\Routing\CachedUrlGenerator. * * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator - * The wrapped url generator + * The wrapped URL generator * @param \Drupal\Core\Cache\CacheBackendInterface $cache * The cache backend. + * @param \Drupal\Core\Language\LanguageManager $language_manager + * The language manager. */ - public function __construct(UrlGeneratorInterface $url_generator, CacheBackendInterface $cache) { + public function __construct(UrlGeneratorInterface $url_generator, CacheBackendInterface $cache, LanguageManager $language_manager) { $this->urlGenerator = $url_generator; $this->cache = $cache; + $this->languageManager = $language_manager; } /** - * Clears the caches of the url generator. + * Clears the caches of the URL generator. */ public function clear() { $this->cachedUrls = array(); @@ -97,57 +108,45 @@ protected function writeCache() { /** * {@inheritdoc} */ - public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH) { - // We can only cache the url if $name is a string, not the actual route - // object. - $parameter_hash = hash('sha256', serialize($parameters)) . (string) $referenceType; - if (is_scalar($name) && isset($this->cachedUrls[self::ROUTE_CACHE_PREFIX . $name . $parameter_hash])) { - return $this->cachedUrls[self::ROUTE_CACHE_PREFIX . $name . $parameter_hash]; - } - $url = $this->urlGenerator->generate($name, $parameters, $referenceType); - if (is_scalar($name)) { - // Cache the url for this route. - $this->cachedUrls[self::ROUTE_CACHE_PREFIX . $name . $parameter_hash] = $url; - $this->cacheNeedsWriting = TRUE; - } - return $url; + public function generate($name, $parameters = array(), $absolute = FALSE) { + $options['absolute'] = $absolute; + return $this->generateFromRoute($name, $parameters, $options); } /** * {@inheritdoc} */ public function generateFromPath($path = NULL, $options = array()) { - $options_hash = hash('sha256', serialize($options)); - if (isset($this->cachedUrls[self::PATH_CACHE_PREFIX . $path . $options_hash])) { - return $this->cachedUrls[self::PATH_CACHE_PREFIX . $path . $options_hash]; + $key = self::PATH_CACHE_PREFIX . hash('sha256', $path . serialize($options)); + if (!isset($this->cachedUrls[$key])) { + $this->cachedUrls[$key] = $this->urlGenerator->generateFromPath($path, $options); + $this->cacheNeedsWriting = TRUE; } - $url = $this->urlGenerator->generateFromPath($path, $options); - // Cache the url for this route. - $this->cachedUrls[self::PATH_CACHE_PREFIX . $path . $options_hash] = $url; - $this->cacheNeedsWriting = TRUE; - return $url; + return $this->cachedUrls[$key]; } /** * {@inheritdoc} */ public function generateFromRoute($name, $parameters = array(), $options = array()) { - $hash = hash('sha256', serialize($name) . serialize($options) . serialize($parameters)); - if (isset($this->cachedUrls[self::PATH_CACHE_PREFIX . $hash])) { - return $this->cachedUrls[self::PATH_CACHE_PREFIX . $hash]; + // In some cases $name may be a Route object, rather than a string. + $key = self::ROUTE_CACHE_PREFIX . hash('sha256', serialize($name) . serialize($options) . serialize($parameters)); + if (!isset($this->cachedUrls[$key])) { + $this->cachedUrls[$key] = $this->urlGenerator->generateFromRoute($name, $parameters, $options); + $this->cacheNeedsWriting = TRUE; } - $url = $this->urlGenerator->generateFromRoute($name, $parameters, $options); - // Cache the url for this route. - $this->cachedUrls[self::PATH_CACHE_PREFIX . $hash] = $url; - $this->cacheNeedsWriting = TRUE; - return $url; + return $this->cachedUrls[$key]; } /** * {@inheritdoc} */ public function setRequest(Request $request) { - $this->cacheKey = $request->attributes->get('system_path'); + $this->cacheKey = $request->attributes->get('_system_path'); + // Only multilingual sites have language dependant URLs. + if ($this->languageManager->isMultilingual()) { + $this->cacheKey .= '::' . $this->languageManager->getLanguage(Language::TYPE_URL); + } $cached = $this->cache->get($this->cacheKey); if ($cached) { $this->cachedUrls = $cached->data; diff --git a/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorTest.php b/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorTest.php index f1a8a14..7ecf07c 100644 --- a/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorTest.php @@ -30,6 +30,13 @@ class CachedUrlGeneratorTest extends UnitTestCase { protected $cache; /** + * Language manager for retrieving the URL language type. + * + * @var \Drupal\Core\Language\LanguageManager|\PHPUnit_Framework_MockObject_MockObject + */ + protected $languageManager; + + /** * The actual tested cached url generator. * * @var \Drupal\Core\Routing\CachedUrlGenerator @@ -52,8 +59,9 @@ protected function setUp() { $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface'); $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); + $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManager'); - $this->cachedUrlGenerator = new CachedUrlGenerator($this->urlGenerator, $this->cache); + $this->cachedUrlGenerator = new CachedUrlGenerator($this->urlGenerator, $this->cache, $this->languageManager); } /** @@ -63,11 +71,11 @@ protected function setUp() { */ public function testGenerate() { $this->urlGenerator->expects($this->once()) - ->method('generate') + ->method('generateFromRoute') ->with('test_route') ->will($this->returnValue('test-route-1')); - $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generate('test_route')); - $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generate('test_route')); + $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generateFromRoute('test_route')); + $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generateFromRoute('test_route')); } /** @@ -77,15 +85,15 @@ public function testGenerate() { */ public function testGenerateWithDifferentParameters() { $this->urlGenerator->expects($this->exactly(2)) - ->method('generate') + ->method('generateFromRoute') ->will($this->returnValueMap(array( - array('test_route', array('key' => 'value1'), CachedUrlGenerator::ABSOLUTE_PATH, 'test-route-1/value1'), - array('test_route', array('key' => 'value2'), CachedUrlGenerator::ABSOLUTE_PATH, 'test-route-1/value2'), + array('test_route', array('key' => 'value1'), array(), 'test-route-1/value1'), + array('test_route', array('key' => 'value2'), array(), 'test-route-1/value2'), ))); - $this->assertEquals('test-route-1/value1', $this->cachedUrlGenerator->generate('test_route', array('key' => 'value1'))); - $this->assertEquals('test-route-1/value1', $this->cachedUrlGenerator->generate('test_route', array('key' => 'value1'))); - $this->assertEquals('test-route-1/value2', $this->cachedUrlGenerator->generate('test_route', array('key' => 'value2'))); - $this->assertEquals('test-route-1/value2', $this->cachedUrlGenerator->generate('test_route', array('key' => 'value2'))); + $this->assertEquals('test-route-1/value1', $this->cachedUrlGenerator->generateFromRoute('test_route', array('key' => 'value1'))); + $this->assertEquals('test-route-1/value1', $this->cachedUrlGenerator->generateFromRoute('test_route', array('key' => 'value1'))); + $this->assertEquals('test-route-1/value2', $this->cachedUrlGenerator->generateFromRoute('test_route', array('key' => 'value2'))); + $this->assertEquals('test-route-1/value2', $this->cachedUrlGenerator->generateFromRoute('test_route', array('key' => 'value2'))); } /**