annotate core/lib/Drupal/Core/Utility/ThemeRegistry.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 1fec387a4317
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Core\Utility;
Chris@0 4
Chris@0 5 use Drupal\Core\Cache\Cache;
Chris@0 6 use Drupal\Core\Cache\CacheBackendInterface;
Chris@0 7 use Drupal\Core\Cache\CacheCollector;
Chris@0 8 use Drupal\Core\DestructableInterface;
Chris@0 9 use Drupal\Core\Lock\LockBackendInterface;
Chris@0 10
Chris@0 11 /**
Chris@0 12 * Builds the run-time theme registry.
Chris@0 13 *
Chris@0 14 * A cache collector to allow the theme registry to be accessed as a
Chris@0 15 * complete registry, while internally caching only the parts of the registry
Chris@0 16 * that are actually in use on the site. On cache misses the complete
Chris@0 17 * theme registry is loaded and used to update the run-time cache.
Chris@0 18 */
Chris@0 19 class ThemeRegistry extends CacheCollector implements DestructableInterface {
Chris@0 20
Chris@0 21 /**
Chris@0 22 * Whether the partial registry can be persisted to the cache.
Chris@0 23 *
Chris@0 24 * This is only allowed if all modules and the request method is GET. _theme()
Chris@0 25 * should be very rarely called on POST requests and this avoids polluting
Chris@0 26 * the runtime cache.
Chris@0 27 */
Chris@0 28 protected $persistable;
Chris@0 29
Chris@0 30 /**
Chris@0 31 * The complete theme registry array.
Chris@0 32 */
Chris@0 33 protected $completeRegistry;
Chris@0 34
Chris@0 35 /**
Chris@0 36 * Constructs a ThemeRegistry object.
Chris@0 37 *
Chris@0 38 * @param string $cid
Chris@0 39 * The cid for the array being cached.
Chris@0 40 * @param \Drupal\Core\Cache\CacheBackendInterface $cache
Chris@0 41 * The cache backend.
Chris@0 42 * @param \Drupal\Core\Lock\LockBackendInterface $lock
Chris@0 43 * The lock backend.
Chris@0 44 * @param array $tags
Chris@0 45 * (optional) The tags to specify for the cache item.
Chris@0 46 * @param bool $modules_loaded
Chris@0 47 * Whether all modules have already been loaded.
Chris@0 48 */
Chris@0 49 public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, $tags = [], $modules_loaded = FALSE) {
Chris@0 50 $this->cid = $cid;
Chris@0 51 $this->cache = $cache;
Chris@0 52 $this->lock = $lock;
Chris@0 53 $this->tags = $tags;
Chris@0 54 $this->persistable = $modules_loaded && \Drupal::hasRequest() && \Drupal::request()->isMethod('GET');
Chris@0 55
Chris@0 56 // @todo: Implement lazyload.
Chris@0 57 $this->cacheLoaded = TRUE;
Chris@0 58
Chris@0 59 if ($this->persistable && $cached = $this->cache->get($this->cid)) {
Chris@0 60 $this->storage = $cached->data;
Chris@0 61 }
Chris@0 62 else {
Chris@0 63 // If there is no runtime cache stored, fetch the full theme registry,
Chris@0 64 // but then initialize each value to NULL. This allows offsetExists()
Chris@0 65 // to function correctly on non-registered theme hooks without triggering
Chris@0 66 // a call to resolveCacheMiss().
Chris@0 67 $this->storage = $this->initializeRegistry();
Chris@0 68 foreach (array_keys($this->storage) as $key) {
Chris@0 69 $this->persist($key);
Chris@0 70 }
Chris@0 71 // RegistryTest::testRaceCondition() ensures that the cache entry is
Chris@0 72 // written on the initial construction of the theme registry.
Chris@0 73 $this->updateCache();
Chris@0 74 }
Chris@0 75 }
Chris@0 76
Chris@0 77 /**
Chris@0 78 * Initializes the full theme registry.
Chris@0 79 *
Chris@0 80 * @return
Chris@0 81 * An array with the keys of the full theme registry, but the values
Chris@0 82 * initialized to NULL.
Chris@0 83 */
Chris@0 84 public function initializeRegistry() {
Chris@0 85 // @todo DIC this.
Chris@0 86 $this->completeRegistry = \Drupal::service('theme.registry')->get();
Chris@0 87
Chris@0 88 return array_fill_keys(array_keys($this->completeRegistry), NULL);
Chris@0 89 }
Chris@0 90
Chris@0 91 /**
Chris@0 92 * {@inheritdoc}
Chris@0 93 */
Chris@0 94 public function has($key) {
Chris@0 95 // Since the theme registry allows for theme hooks to be requested that
Chris@0 96 // are not registered, just check the existence of the key in the registry.
Chris@0 97 // Use array_key_exists() here since a NULL value indicates that the theme
Chris@0 98 // hook exists but has not yet been requested.
Chris@14 99 return isset($this->storage[$key]) || array_key_exists($key, $this->storage);
Chris@0 100 }
Chris@0 101
Chris@0 102 /**
Chris@0 103 * {@inheritdoc}
Chris@0 104 */
Chris@0 105 public function get($key) {
Chris@0 106 // If the offset is set but empty, it is a registered theme hook that has
Chris@0 107 // not yet been requested. Offsets that do not exist at all were not
Chris@0 108 // registered in hook_theme().
Chris@0 109 if (isset($this->storage[$key])) {
Chris@0 110 return $this->storage[$key];
Chris@0 111 }
Chris@0 112 elseif (array_key_exists($key, $this->storage)) {
Chris@0 113 return $this->resolveCacheMiss($key);
Chris@0 114 }
Chris@0 115 }
Chris@0 116
Chris@0 117 /**
Chris@0 118 * {@inheritdoc}
Chris@0 119 */
Chris@0 120 public function resolveCacheMiss($key) {
Chris@0 121 if (!isset($this->completeRegistry)) {
Chris@0 122 $this->completeRegistry = \Drupal::service('theme.registry')->get();
Chris@0 123 }
Chris@0 124 $this->storage[$key] = $this->completeRegistry[$key];
Chris@0 125 if ($this->persistable) {
Chris@0 126 $this->persist($key);
Chris@0 127 }
Chris@0 128 return $this->storage[$key];
Chris@0 129 }
Chris@0 130
Chris@0 131 /**
Chris@0 132 * {@inheritdoc}
Chris@0 133 */
Chris@0 134 protected function updateCache($lock = TRUE) {
Chris@0 135 if (!$this->persistable) {
Chris@0 136 return;
Chris@0 137 }
Chris@0 138 // @todo: Is the custom implementation necessary?
Chris@0 139 $data = [];
Chris@0 140 foreach ($this->keysToPersist as $offset => $persist) {
Chris@0 141 if ($persist) {
Chris@0 142 $data[$offset] = $this->storage[$offset];
Chris@0 143 }
Chris@0 144 }
Chris@0 145 if (empty($data)) {
Chris@0 146 return;
Chris@0 147 }
Chris@0 148
Chris@0 149 $lock_name = $this->cid . ':' . __CLASS__;
Chris@0 150 if (!$lock || $this->lock->acquire($lock_name)) {
Chris@0 151 if ($cached = $this->cache->get($this->cid)) {
Chris@0 152 // Use array merge instead of union so that filled in values in $data
Chris@0 153 // overwrite empty values in the current cache.
Chris@0 154 $data = array_merge($cached->data, $data);
Chris@0 155 }
Chris@0 156 else {
Chris@0 157 $registry = $this->initializeRegistry();
Chris@0 158 $data = array_merge($registry, $data);
Chris@0 159 }
Chris@0 160 $this->cache->set($this->cid, $data, Cache::PERMANENT, $this->tags);
Chris@0 161 if ($lock) {
Chris@0 162 $this->lock->release($lock_name);
Chris@0 163 }
Chris@0 164 }
Chris@0 165 }
Chris@0 166
Chris@0 167 }