summaryrefslogtreecommitdiffstatshomepage
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/common.inc216
1 files changed, 216 insertions, 0 deletions
diff --git a/includes/common.inc b/includes/common.inc
index ca143d457816..7faf03225920 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -77,6 +77,65 @@ define('JS_THEME', 100);
define('HTTP_REQUEST_TIMEOUT', 1);
/**
+ * Constants defining cache granularity for blocks and renderable arrays.
+ *
+ * Modules specify the caching patterns for their blocks using binary
+ * combinations of these constants in their hook_block_info():
+ * $block[delta]['cache'] = DRUPAL_CACHE_PER_ROLE | DRUPAL_CACHE_PER_PAGE;
+ * DRUPAL_CACHE_PER_ROLE is used as a default when no caching pattern is
+ * specified. Use DRUPAL_CACHE_CUSTOM to disable standard block cache and
+ * implement
+ *
+ * The block cache is cleared in cache_clear_all(), and uses the same clearing
+ * policy than page cache (node, comment, user, taxonomy added or updated...).
+ * Blocks requiring more fine-grained clearing might consider disabling the
+ * built-in block cache (DRUPAL_NO_CACHE) and roll their own.
+ *
+ * Note that user 1 is excluded from block caching.
+ */
+
+/**
+ * The block should not get cached. This setting should be used:
+ * - for simple blocks (notably those that do not perform any db query),
+ * where querying the db cache would be more expensive than directly generating
+ * the content.
+ * - for blocks that change too frequently.
+ */
+define('DRUPAL_NO_CACHE', -1);
+
+/**
+ * The block is handling its own caching in its hook_block_view(). From the
+ * perspective of the block cache system, this is equivalent to DRUPAL_NO_CACHE.
+ * Useful when time based expiration is needed or a site uses a node access
+ * which invalidates standard block cache.
+ */
+define('DRUPAL_CACHE_CUSTOM', -2);
+
+/**
+ * The block or element can change depending on the roles the user viewing the
+ * page belongs to. This is the default setting for blocks, used when the block
+ * does not specify anything.
+ */
+define('DRUPAL_CACHE_PER_ROLE', 0x0001);
+
+/**
+ * The block or element can change depending on the user viewing the page.
+ * This setting can be resource-consuming for sites with large number of users,
+ * and thus should only be used when DRUPAL_CACHE_PER_ROLE is not sufficient.
+ */
+define('DRUPAL_CACHE_PER_USER', 0x0002);
+
+/**
+ * The block or element can change depending on the page being viewed.
+ */
+define('DRUPAL_CACHE_PER_PAGE', 0x0004);
+
+/**
+ * The block or element is the same for every user on every page where it is visible.
+ */
+define('DRUPAL_CACHE_GLOBAL', 0x0008);
+
+/**
* Add content to a specified region.
*
* @param $region
@@ -3947,6 +4006,24 @@ function drupal_render_page($page) {
* the placement of the form's children while not at all having to deal with
* the form markup itself.
*
+ * drupal_render() can optionally cache the rendered output of elements to
+ * improve performance. To use drupal_render() caching, set the element's #cache
+ * property to an associative array with one or several of the following keys:
+ * - 'keys': An array of one or more keys that identify the element. If 'keys'
+ * is set, the cache ID is created automatically from these keys.
+ * @see drupal_render_cid_create()
+ * - 'granularity' (optional): Define the cache granularity using binary
+ * combinations of the cache granularity constants, e.g. DRUPAL_CACHE_PER_USER
+ * to cache for each user seperately or
+ * DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE to cache seperately for each
+ * page and role. If not specified the element is cached globally for each
+ * theme and language.
+ * - 'cid': Specify the cache ID directly. Either 'keys' or 'cid' is required.
+ * If 'cid' is set, 'keys' and 'granularity' are ignored. Use only if you
+ * have special requirements.
+ * - 'expire': Set to one of the cache lifetime constants.
+ * - 'bin': Specify a cache bin to cache the element in. Defaults to 'cache'.
+ *
* This function is usually called from within another function, like
* drupal_get_form() or a theme function. Elements are sorted internally
* using uasort(). Since this is expensive, when passing already sorted
@@ -3970,6 +4047,11 @@ function drupal_render(&$elements) {
return;
}
+ // Try to fetch the element's markup from cache and return.
+ if ($cached_output = drupal_render_cache_get($elements)) {
+ return $cached_output;
+ }
+
// If the default values for this element have not been loaded yet, populate
// them.
if (isset($elements['#type']) && empty($elements['#defaults_loaded'])) {
@@ -4043,6 +4125,11 @@ function drupal_render(&$elements) {
$prefix = isset($elements['#prefix']) ? $elements['#prefix'] : '';
$suffix = isset($elements['#suffix']) ? $elements['#suffix'] : '';
+ // Cache the processed element if #cache is set.
+ if (isset($elements['#cache'])) {
+ drupal_render_cache_set($prefix . $elements['#children'] . $suffix, $elements);
+ }
+
$elements['#printed'] = TRUE;
return $prefix . $elements['#children'] . $suffix;
}
@@ -4121,6 +4208,135 @@ function show(&$element) {
}
/**
+ * Get the rendered output of a renderable element from cache.
+ *
+ * @see drupal_render()
+ * @see drupal_render_cache_set()
+ *
+ * @param $elements
+ * A renderable array.
+ * @return
+ * A markup string containing the rendered content of the element, or FALSE
+ * if no cached copy of the element is available.
+ */
+function drupal_render_cache_get($elements) {
+ if (!in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD')) || !$cid = drupal_render_cid_create($elements)) {
+ return FALSE;
+ }
+ $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'cache';
+
+ if (!empty($cid) && $cache = cache_get($cid, $bin)) {
+ // Add additional libraries, CSS and JavaScript associated with this element.
+ drupal_process_attached(
+ isset($cache->data['#attached_library']) ? $cache->data['#attached_library'] : array(),
+ isset($cache->data['#attached_js']) ? $cache->data['#attached_js'] : array(),
+ isset($cache->data['#attached_css']) ? $cache->data['#attached_css'] : array()
+ );
+ // Return the rendered output.
+ return $cache->data['#markup'];;
+ }
+ return FALSE;
+}
+
+/**
+ * Cache the rendered output of a renderable element.
+ *
+ * This is called by drupal_render() if the #cache property is set on an element.
+ *
+ * @see drupal_render()
+ * @see drupal_render_cache_get()
+ *
+ * @param $markup
+ * The rendered output string of $elements.
+ * @param $elements
+ * A renderable array.
+ */
+function drupal_render_cache_set($markup, $elements) {
+ // Create the cache ID for the element
+ if (!in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD')) || !$cid = drupal_render_cid_create($elements)) {
+ return FALSE;
+ }
+
+ $data['#markup'] = $markup;
+ // Persist additional CSS and JavaScript files associated with this element.
+ foreach (array('css', 'js', 'library') as $kind) {
+ if (!empty($elements['#attached_' . $kind])) {
+ $data['#attached_' . $kind] = $elements['#attached_' . $kind];
+ }
+ }
+ $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'cache';
+ $expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : CACHE_PERMANENT;
+ cache_set($cid, $data, $bin, $expire);
+}
+
+/**
+ * Helper function for building cache ids.
+ *
+ * @param $granularity
+ * One or more cache granularity constants, e.g. DRUPAL_CACHE_PER_USER to cache
+ * for each user seperately or DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE to
+ * cache seperately for each page and role.
+ *
+ * @return
+ * An array of cache ID parts, always containing the active theme. If the
+ * locale module is enabled it also contains the active language. If
+ * $granularity was passed in, more parts are added.
+ */
+function drupal_render_cid_parts($granularity = NULL) {
+ global $theme, $base_root, $user;
+
+ $cid_parts[] = $theme;
+ if (module_exists('locale')) {
+ global $language;
+ $cid_parts[] = $language->language;
+ }
+
+ if (!empty($granularity)) {
+ // 'PER_ROLE' and 'PER_USER' are mutually exclusive. 'PER_USER' can be a
+ // resource drag for sites with many users, so when a module is being
+ // equivocal, we favor the less expensive 'PER_ROLE' pattern.
+ if ($granularity & DRUPAL_CACHE_PER_ROLE) {
+ $cid_parts[] = 'r.' . implode(',', array_keys($user->roles));
+ }
+ elseif ($granularity & DRUPAL_CACHE_PER_USER) {
+ $cid_parts[] = "u.$user->uid";
+ }
+
+ if ($granularity & DRUPAL_CACHE_PER_PAGE) {
+ $cid_parts[] = $base_root . request_uri();
+ }
+ }
+
+ return $cid_parts;
+}
+
+/**
+ * Create the cache ID for a renderable element.
+ *
+ * This creates the cache ID string, either by returning the #cache['cid']
+ * property if present or by building the cache ID out of the #cache['keys']
+ * and, optionally, the #cache['granularity'] properties.
+ *
+ * @param $elements
+ * A renderable array.
+ *
+ * @return
+ * The cache ID string, or FALSE if the element may not be cached.
+ */
+function drupal_render_cid_create($elements) {
+ if (isset($elements['#cache']['cid'])) {
+ return $elements['#cache']['cid'];
+ }
+ elseif (isset($elements['#cache']['keys'])) {
+ $granularity = isset($elements['#cache']['granularity']) ? $elements['#cache']['granularity'] : NULL;
+ // Merge in additional cache ID parts based provided by drupal_render_cid_parts().
+ $cid_parts = array_merge($elements['#cache']['keys'], drupal_render_cid_parts($granularity));
+ return implode(':', $cid_parts);
+ }
+ return FALSE;
+}
+
+/**
* Function used by uasort to sort structured arrays by weight.
*/
function element_sort($a, $b) {