123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- <?php
- namespace Drupal\Core\Utility;
- use Drupal\Core\Cache\Cache;
- use Drupal\Core\Cache\CacheBackendInterface;
- use Drupal\Core\Cache\CacheCollector;
- use Drupal\Core\DestructableInterface;
- use Drupal\Core\Lock\LockBackendInterface;
- /**
- * Builds the run-time theme registry.
- *
- * A cache collector to allow the theme registry to be accessed as a
- * complete registry, while internally caching only the parts of the registry
- * that are actually in use on the site. On cache misses the complete
- * theme registry is loaded and used to update the run-time cache.
- */
- class ThemeRegistry extends CacheCollector implements DestructableInterface {
- /**
- * Whether the partial registry can be persisted to the cache.
- *
- * This is only allowed if all modules and the request method is GET. _theme()
- * should be very rarely called on POST requests and this avoids polluting
- * the runtime cache.
- */
- protected $persistable;
- /**
- * The complete theme registry array.
- */
- protected $completeRegistry;
- /**
- * 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 array $tags
- * (optional) The tags to specify for the cache item.
- * @param bool $modules_loaded
- * Whether all modules have already been loaded.
- */
- public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, $tags = [], $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;
- if ($this->persistable && $cached = $this->cache->get($this->cid)) {
- $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();
- }
- }
- /**
- * Initializes the full theme registry.
- *
- * @return
- * An array with the keys of the full theme registry, but the values
- * initialized to NULL.
- */
- public function initializeRegistry() {
- // @todo DIC this.
- $this->completeRegistry = \Drupal::service('theme.registry')->get();
- return array_fill_keys(array_keys($this->completeRegistry), NULL);
- }
- /**
- * {@inheritdoc}
- */
- public function has($key) {
- // Since the theme registry allows for theme hooks to be requested that
- // 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 isset($this->storage[$key]) || array_key_exists($key, $this->storage);
- }
- /**
- * {@inheritdoc}
- */
- 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];
- }
- elseif (array_key_exists($key, $this->storage)) {
- return $this->resolveCacheMiss($key);
- }
- }
- /**
- * {@inheritdoc}
- */
- public function resolveCacheMiss($key) {
- if (!isset($this->completeRegistry)) {
- $this->completeRegistry = \Drupal::service('theme.registry')->get();
- }
- $this->storage[$key] = $this->completeRegistry[$key];
- if ($this->persistable) {
- $this->persist($key);
- }
- return $this->storage[$key];
- }
- /**
- * {@inheritdoc}
- */
- protected function updateCache($lock = TRUE) {
- if (!$this->persistable) {
- return;
- }
- // @todo: Is the custom implementation necessary?
- $data = [];
- foreach ($this->keysToPersist as $offset => $persist) {
- if ($persist) {
- $data[$offset] = $this->storage[$offset];
- }
- }
- if (empty($data)) {
- return;
- }
- $lock_name = $this->cid . ':' . __CLASS__;
- if (!$lock || $this->lock->acquire($lock_name)) {
- if ($cached = $this->cache->get($this->cid)) {
- // 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);
- }
- else {
- $registry = $this->initializeRegistry();
- $data = array_merge($registry, $data);
- }
- $this->cache->set($this->cid, $data, Cache::PERMANENT, $this->tags);
- if ($lock) {
- $this->lock->release($lock_name);
- }
- }
- }
- }
|