diff --git a/core/lib/Drupal/Core/Utility/ThemeRegistry.php b/core/lib/Drupal/Core/Utility/ThemeRegistry.php index edc0089..ad26945 100644 --- a/core/lib/Drupal/Core/Utility/ThemeRegistry.php +++ b/core/lib/Drupal/Core/Utility/ThemeRegistry.php @@ -30,16 +30,33 @@ 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 @@ -52,31 +69,52 @@ class ThemeRegistry extends CacheCollector implements DestructableInterface { * Whether all modules have already been loaded. */ function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, $tags = array(), $modules_loaded = FALSE) { - $this->cid = $cid; $this->cache = $cache; $this->lock = $lock; $this->tags = $tags; - $this->persistable = $modules_loaded && \Drupal::hasRequest() && \Drupal::request()->isMethod('GET'); - - // @todo: Implement lazyload. - $this->cacheLoaded = TRUE; + $this->cid = $cid; + $this->modulesLoaded = $modules_loaded; + } - if ($this->persistable && $cached = $this->cache->get($this->cid)) { - $this->storage = $cached->data; + /** + * 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 +139,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 +149,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 +165,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 +202,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..777f33d 100644 --- a/core/modules/system/src/Tests/Theme/RegistryTest.php +++ b/core/modules/system/src/Tests/Theme/RegistryTest.php @@ -41,11 +41,13 @@ function testRaceCondition() { $cache = \Drupal::cache(); $lock_backend = \Drupal::lock(); $registry = new ThemeRegistry($cid, $cache, $lock_backend, array('theme_registry'), $this->container->get('module_handler')->isLoaded()); - - $this->assertTrue(\Drupal::cache()->get($cid), 'Cache entry was created.'); + // Test that lazy loading works. + $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.'); + // After registry has been actually used we should have a cache entry. + $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