diff core/lib/Drupal/Core/Utility/ThemeRegistry.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 1fec387a4317
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/lib/Drupal/Core/Utility/ThemeRegistry.php	Wed Nov 29 16:09:58 2017 +0000
@@ -0,0 +1,167 @@
+<?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 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);
+      }
+    }
+  }
+
+}