diff options
Diffstat (limited to 'includes/theme.inc')
-rw-r--r-- | includes/theme.inc | 189 |
1 files changed, 165 insertions, 24 deletions
diff --git a/includes/theme.inc b/includes/theme.inc index 252eec081..da4200e56 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,116 @@ 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 { + // 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 = $this->initializeRegistry(); + if ($this->persistable) { + $this->set($data); + } + } + $this->storage = $data; + } + + /** + * Initializes the full theme registry. + * + * @return + * An array with the keys of the full theme registry, but the values + * initialized to NULL. + */ + function initializeRegistry() { + $this->completeRegistry = theme_get_registry(); + + return array_fill_keys(array_keys($this->completeRegistry), NULL); + } + + 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($data, $lock = TRUE) { + $lock_name = $this->cid . ':' . $this->bin; + if (!$lock || lock_acquire($lock_name)) { + if ($cached = cache_get($this->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); + } + else { + $registry = $this->initializeRegistry(); + $data = array_merge($registry, $data); + } + cache_set($this->cid, $data, $this->bin); + if ($lock) { + lock_release($lock_name); + } + } + } +} + +/** * Process a single implementation of hook_theme(). * * @param $cache @@ -657,8 +793,9 @@ function list_themes($refresh = FALSE) { * Generates themed output. * * All requests for themed output must go through this function. It examines - * the request and routes it to the appropriate theme function or template, by - * checking the theme registry. + * the request and routes it to the appropriate + * @link themeable theme function or template @endlink, by checking the theme + * registry. * * The first argument to this function is the name of the theme hook. For * instance, to theme a table, the theme hook name is 'table'. By default, this @@ -758,6 +895,8 @@ function list_themes($refresh = FALSE) { * * @return * An HTML string representing the themed output. + * + * @see themeable */ function theme($hook, $variables = array()) { static $hooks = NULL; @@ -771,7 +910,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 @@ -1391,7 +1530,7 @@ function theme_link($variables) { * @param $variables * An associative array containing: * - links: An associative array of links to be themed. The key for each link - * is used as its css class. Each link should be itself an array, with the + * is used as its CSS class. Each link should be itself an array, with the * following elements: * - title: The link text. * - href: The link URL. If omitted, the 'title' is shown as a plain text @@ -1981,6 +2120,8 @@ function theme_username($variables) { /** * Returns HTML for a progress bar. * + * Note that the core Batch API uses this only for non-JavaScript batch jobs. + * * @param $variables * An associative array containing: * - percent: The percentage of the progress. |