diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php index 5e450ab..2687206 100644 --- a/core/lib/Drupal/Core/Theme/Registry.php +++ b/core/lib/Drupal/Core/Theme/Registry.php @@ -236,7 +236,7 @@ public function get() { public function getRuntime() { $this->init($this->themeName); if (!isset($this->runtimeRegistry)) { - $this->runtimeRegistry = new ThemeRegistry('theme_registry:runtime:' . $this->theme->getName(), $this->cache, $this->lock, array('theme_registry'), $this->moduleHandler->isLoaded()); + $this->runtimeRegistry = new ThemeRegistry($this->cache, $this->lock, $this->theme, array('theme_registry'), $this->moduleHandler->isLoaded()); } return $this->runtimeRegistry; } diff --git a/core/lib/Drupal/Core/Utility/ThemeRegistry.php b/core/lib/Drupal/Core/Utility/ThemeRegistry.php index edc0089..7bbc4c0 100644 --- a/core/lib/Drupal/Core/Utility/ThemeRegistry.php +++ b/core/lib/Drupal/Core/Utility/ThemeRegistry.php @@ -12,6 +12,7 @@ use Drupal\Core\Cache\CacheCollector; use Drupal\Core\DestructableInterface; use Drupal\Core\Lock\LockBackendInterface; +use Drupal\Core\Theme\ActiveTheme; /** * Builds the run-time theme registry. @@ -30,53 +31,102 @@ class ThemeRegistry extends CacheCollector implements DestructableInterface { * should be very rarely called on POST requests and this avoids polluting * the runtime cache. */ - protected $persistable; + protected $persistable = NULL; /** * The complete theme registry array. + * + * @var array */ protected $completeRegistry; /** + * Whether all modules have already been loaded. + * + * @var bool + */ + protected $modulesLoaded; + + /** + * The theme object representing the active theme for this registry. + * + * @var \Drupal\Core\Theme\ActiveTheme + */ + protected $theme; + + /** * Constructs a ThemeRegistry object. * - * @param string $cid - * The cid for the array being cached. * @param \Drupal\Core\Cache\CacheBackendInterface $cache * The cache backend. * @param \Drupal\Core\Lock\LockBackendInterface $lock * The lock backend. + * @param \Drupal\Core\Theme\ActiveTheme $theme + * The theme object representing the active theme for this registry. * @param array $tags * (optional) The tags to specify for the cache item. * @param bool $modules_loaded * Whether all modules have already been loaded. */ - function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, $tags = array(), $modules_loaded = FALSE) { - $this->cid = $cid; + function __construct(CacheBackendInterface $cache, LockBackendInterface $lock, ActiveTheme $theme, $tags = array(), $modules_loaded = FALSE) { $this->cache = $cache; $this->lock = $lock; $this->tags = $tags; - $this->persistable = $modules_loaded && \Drupal::hasRequest() && \Drupal::request()->isMethod('GET'); + $this->theme = $theme; + $this->modulesLoaded = $modules_loaded; + } - // @todo: Implement lazyload. - $this->cacheLoaded = TRUE; + /** + * {@inheritdoc} + */ + protected function getCid() { + if (!$this->cid) { + $this->cid = 'theme_registry:runtime:' . $this->theme->getName(); + } - if ($this->persistable && $cached = $this->cache->get($this->cid)) { - $this->storage = $cached->data; + return $this->cid; + } + + /** + * Gets the persistable. + * + * @return bool + */ + protected function getPersistable() { + if ($this->persistable === NULL) { + $this->persistable = $this->modulesLoaded && \Drupal::hasRequest() && \Drupal::request()->isMethod('GET'); } - else { - // If there is no runtime cache stored, fetch the full theme registry, - // but then initialize each value to NULL. This allows offsetExists() - // to function correctly on non-registered theme hooks without triggering - // a call to resolveCacheMiss(). - $this->storage = $this->initializeRegistry(); - foreach (array_keys($this->storage) as $key) { - $this->persist($key); + + return $this->persistable; + } + + + /** + * Gets the storage. + * + * @return array + */ + protected function getStorage() { + if (!$this->storage) { + if ($this->getPersistable() && $cached = $this->cache->get($this->getCid())) { + $this->storage = $cached->data; + } + else { + // If there is no runtime cache stored, fetch the full theme registry, + // but then initialize each value to NULL. This allows offsetExists() + // to function correctly on non-registered theme hooks without + // triggering a call to resolveCacheMiss(). + $this->storage = $this->initializeRegistry(); + foreach (array_keys($this->storage) as $key) { + $this->persist($key); + } + // RegistryTest::testRaceCondition() ensures that the cache entry is + // written on the initial construction of the theme registry. + $this->updateCache(); } - // RegistryTest::testRaceCondition() ensures that the cache entry is - // written on the initial construction of the theme registry. - $this->updateCache(); } + + return $this->storage; } /** @@ -101,7 +151,7 @@ public function has($key) { // are not registered, just check the existence of the key in the registry. // Use array_key_exists() here since a NULL value indicates that the theme // hook exists but has not yet been requested. - return array_key_exists($key, $this->storage); + return array_key_exists($key, $this->getStorage()); } /** @@ -111,10 +161,11 @@ public function get($key) { // If the offset is set but empty, it is a registered theme hook that has // not yet been requested. Offsets that do not exist at all were not // registered in hook_theme(). - if (isset($this->storage[$key])) { - return $this->storage[$key]; + $storage = $this->getStorage(); + if (isset($storage[$key])) { + return $storage[$key]; } - elseif (array_key_exists($key, $this->storage)) { + elseif (array_key_exists($key, $storage)) { return $this->resolveCacheMiss($key); } } @@ -126,34 +177,35 @@ public function resolveCacheMiss($key) { if (!isset($this->completeRegistry)) { $this->completeRegistry = \Drupal::service('theme.registry')->get(); } - $this->storage[$key] = $this->completeRegistry[$key]; - if ($this->persistable) { + $storage = $this->getStorage(); + $storage[$key] = $this->completeRegistry[$key]; + if ($this->getPersistable()) { $this->persist($key); } - return $this->storage[$key]; + return $storage[$key]; } /** * {@inheritdoc} */ protected function updateCache($lock = TRUE) { - if (!$this->persistable) { + if (!$this->getPersistable()) { return; } // @todo: Is the custom implementation necessary? $data = array(); foreach ($this->keysToPersist as $offset => $persist) { if ($persist) { - $data[$offset] = $this->storage[$offset]; + $data[$offset] = $this->getStorage()[$offset]; } } if (empty($data)) { return; } - $lock_name = $this->cid . ':' . __CLASS__; + $lock_name = $this->getCid() . ':' . __CLASS__; if (!$lock || $this->lock->acquire($lock_name)) { - if ($cached = $this->cache->get($this->cid)) { + if ($cached = $this->cache->get($this->getCid())) { // Use array merge instead of union so that filled in values in $data // overwrite empty values in the current cache. $data = array_merge($cached->data, $data); @@ -162,7 +214,7 @@ protected function updateCache($lock = TRUE) { $registry = $this->initializeRegistry(); $data = array_merge($registry, $data); } - $this->cache->set($this->cid, $data, Cache::PERMANENT, $this->tags); + $this->cache->set($this->getCid(), $data, Cache::PERMANENT, $this->tags); if ($lock) { $this->lock->release($lock_name); } diff --git a/core/modules/system/src/Tests/Theme/RegistryTest.php b/core/modules/system/src/Tests/Theme/RegistryTest.php index 8872f10..a037071 100644 --- a/core/modules/system/src/Tests/Theme/RegistryTest.php +++ b/core/modules/system/src/Tests/Theme/RegistryTest.php @@ -34,18 +34,20 @@ function testRaceCondition() { // The theme registry is not marked as persistable in case we don't have a // proper request. \Drupal::request()->setMethod('GET'); - $cid = 'test_theme_registry'; // Directly instantiate the theme registry, this will cause a base cache // entry to be written in __construct(). $cache = \Drupal::cache(); $lock_backend = \Drupal::lock(); - $registry = new ThemeRegistry($cid, $cache, $lock_backend, array('theme_registry'), $this->container->get('module_handler')->isLoaded()); + $theme = \Drupal::theme(); + $registry = new ThemeRegistry($cache, $lock_backend, $theme->getActiveTheme(), array('theme_registry'), $this->container->get('module_handler')->isLoaded()); - $this->assertTrue(\Drupal::cache()->get($cid), 'Cache entry was created.'); + $cid = 'theme_registry:runtime:' . $theme->getActiveTheme()->getName(); + $this->assertFalse(\Drupal::cache()->get($cid), 'Cache entry was not created by default.'); // Trigger a cache miss for an offset. $this->assertTrue($registry->get('theme_test_template_test'), 'Offset was returned correctly from the theme registry.'); + $this->assertTrue(\Drupal::cache()->get($cid), 'Cache entry was created.'); // This will cause the ThemeRegistry class to write an updated version of // the cache entry when it is destroyed, usually at the end of the request. // Before that happens, manually delete the cache entry we created earlier @@ -60,7 +62,7 @@ function testRaceCondition() { // Create a new instance of the class. Confirm that both the offset // requested previously, and one that has not yet been requested are both // available. - $registry = new ThemeRegistry($cid, $cache, $lock_backend, array('theme_registry'), $this->container->get('module_handler')->isLoaded()); + $registry = new ThemeRegistry($cache, $lock_backend, $theme->getActiveTheme(), array('theme_registry'), $this->container->get('module_handler')->isLoaded()); $this->assertTrue($registry->get('theme_test_template_test'), 'Offset was returned correctly from the theme registry'); $this->assertTrue($registry->get('theme_test_template_test_2'), 'Offset was returned correctly from the theme registry'); }