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