summaryrefslogtreecommitdiff
path: root/includes/theme.inc
diff options
context:
space:
mode:
authorwebchick <webchick@24967.no-reply.drupal.org>2011-10-29 12:28:23 -0700
committerwebchick <webchick@24967.no-reply.drupal.org>2011-10-29 12:28:23 -0700
commite56afba266a851fc7f9cf29edd831b7ee9a4802e (patch)
treecee5f40ff97e6d28b0fd5057d1deef522564e910 /includes/theme.inc
parent4060a322128849fcaea16b337559ece8090a459f (diff)
downloadbrdo-e56afba266a851fc7f9cf29edd831b7ee9a4802e.tar.gz
brdo-e56afba266a851fc7f9cf29edd831b7ee9a4802e.tar.bz2
Issue #1011614 by catch, pillarsdotnet: Fixed Theme registry can grow too large for MySQL max_allowed_packet() and memcache default slab size.
Diffstat (limited to 'includes/theme.inc')
-rw-r--r--includes/theme.inc166
1 files changed, 145 insertions, 21 deletions
diff --git a/includes/theme.inc b/includes/theme.inc
index 252eec081..3868334ac 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -237,18 +237,33 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb
/**
* Get the theme registry.
*
+ * @param $complete
+ * Optional boolean to indicate whether to return the complete theme registry
+ * array or an instance of the ThemeRegistry class. If TRUE, the complete
+ * theme registry array will be returned. This is useful if you want to
+ * foreach over the whole registry, use array_* functions or inspect it in a
+ * debugger. If FALSE, an instance of the ThemeRegistry class will be
+ * returned, this provides an ArrayObject which allows it to be accessed
+ * with array syntax and isset(), and should be more lightweight
+ * than the full registry. Defaults to TRUE.
+ *
* @return
- * The theme registry array if it has been stored in memory, NULL otherwise.
+ * The complete theme registry array, or an instance of the ThemeRegistry
+ * class.
*/
-function theme_get_registry() {
- static $theme_registry = NULL;
+function theme_get_registry($complete = TRUE) {
+ static $theme_registry = array();
+ $key = (int) $complete;
- if (!isset($theme_registry)) {
+ if (!isset($theme_registry[$key])) {
list($callback, $arguments) = _theme_registry_callback();
- $theme_registry = call_user_func_array($callback, $arguments);
+ if (!$complete) {
+ $arguments[] = FALSE;
+ }
+ $theme_registry[$key] = call_user_func_array($callback, $arguments);
}
- return $theme_registry;
+ return $theme_registry[$key];
}
/**
@@ -268,7 +283,7 @@ function _theme_registry_callback($callback = NULL, array $arguments = array())
}
/**
- * Get the theme_registry cache from the database; if it doesn't exist, build it.
+ * Get the theme_registry cache; if it doesn't exist, build it.
*
* @param $theme
* The loaded $theme object as returned by list_themes().
@@ -277,23 +292,34 @@ function _theme_registry_callback($callback = NULL, array $arguments = array())
* oldest first order.
* @param $theme_engine
* The name of the theme engine.
+ * @param $complete
+ * Whether to load the complete theme registry or an instance of the
+ * ThemeRegistry class.
+ *
+ * @return
+ * The theme registry array, or an instance of the ThemeRegistry class.
*/
-function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL) {
- // Check the theme registry cache; if it exists, use it.
- $cache = cache_get("theme_registry:$theme->name", 'cache');
- if (isset($cache->data)) {
- $registry = $cache->data;
+function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL, $complete = TRUE) {
+ if ($complete) {
+ // Check the theme registry cache; if it exists, use it.
+ $cached = cache_get("theme_registry:$theme->name");
+ if (isset($cached->data)) {
+ $registry = $cached->data;
+ }
+ else {
+ // If not, build one and cache it.
+ $registry = _theme_build_registry($theme, $base_theme, $theme_engine);
+ // Only persist this registry if all modules are loaded. This assures a
+ // complete set of theme hooks.
+ if (module_load_all(NULL)) {
+ _theme_save_registry($theme, $registry);
+ }
+ }
+ return $registry;
}
else {
- // If not, build one and cache it.
- $registry = _theme_build_registry($theme, $base_theme, $theme_engine);
- // Only persist this registry if all modules are loaded. This assures a
- // complete set of theme hooks.
- if (module_load_all(NULL)) {
- _theme_save_registry($theme, $registry);
- }
+ return new ThemeRegistry('theme_registry:runtime:' . $theme->name, 'cache');
}
- return $registry;
}
/**
@@ -313,6 +339,104 @@ function drupal_theme_rebuild() {
}
/**
+ * Builds the run-time theme registry.
+ *
+ * Extends DrupalCacheArray 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 DrupalCacheArray {
+
+ /**
+ * 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;
+
+ function __construct($cid, $bin) {
+ $this->cid = $cid;
+ $this->bin = $bin;
+ $this->persistable = module_load_all(NULL) && $_SERVER['REQUEST_METHOD'] == 'GET';
+
+ $data = array();
+ if ($this->persistable && $cached = cache_get($this->cid, $this->bin)) {
+ $data = $cached->data;
+ }
+ else {
+ $complete_registry = theme_get_registry();
+ if ($this->persistable) {
+ // 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().
+ $data = array_fill_keys(array_keys($complete_registry), NULL);
+ $this->set($this->cid, $data, $this->bin);
+ $this->completeRegistry = $complete_registry;
+ }
+ else {
+ $data = $complete_registry;
+ }
+ }
+ $this->storage = $data;
+ }
+
+ public function offsetExists($offset) {
+ // 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($offset, $this->storage);
+ }
+
+ public function offsetGet($offset) {
+ // 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[$offset])) {
+ return $this->storage[$offset];
+ }
+ elseif (array_key_exists($offset, $this->storage)) {
+ return $this->resolveCacheMiss($offset);
+ }
+ }
+
+ public function resolveCacheMiss($offset) {
+ if (!isset($this->completeRegistry)) {
+ $this->completeRegistry = theme_get_registry();
+ }
+ $this->storage[$offset] = $this->completeRegistry[$offset];
+ if ($this->persistable) {
+ $this->persist($offset);
+ }
+ return $this->storage[$offset];
+ }
+
+ public function set($cid, $data, $bin, $lock = TRUE) {
+ $lock_name = $cid . ':' . $bin;
+ if (!$lock || lock_acquire($lock_name)) {
+ if ($cached = cache_get($cid, $this->bin)) {
+ // 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);
+ }
+ cache_set($cid, $data, $bin);
+ if ($lock) {
+ lock_release($lock_name);
+ }
+ }
+ }
+}
+
+/**
* Process a single implementation of hook_theme().
*
* @param $cache
@@ -771,7 +895,7 @@ function theme($hook, $variables = array()) {
if (!isset($hooks)) {
drupal_theme_initialize();
- $hooks = theme_get_registry();
+ $hooks = theme_get_registry(FALSE);
}
// If an array of hook candidates were passed, use the first one that has an