View source
<?php
namespace Drupal\render_cache\RenderCache\Controller;
use Drupal\render_cache\Cache\Cache;
use Drupal\render_cache\Cache\RenderCacheBackendAdapterInterface;
use Drupal\render_cache\Cache\RenderCachePlaceholder;
use Drupal\render_cache\Cache\RenderStackInterface;
use RenderCache;
abstract class BaseController extends AbstractBaseController {
protected $context = array();
protected $renderStack;
protected $cache;
public function __construct(array $configuration, $plugin_id, $plugin_definition, RenderStackInterface $render_stack, RenderCacheBackendAdapterInterface $cache) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->renderStack = $render_stack;
$this->cache = $cache;
}
public function getContext() {
return $this->context;
}
public function setContext(array $context) {
$this->context = $context;
}
public function viewPlaceholders(array $objects) {
$object_build = array();
if (!empty($objects)) {
$object_build = $this
->render($objects);
}
return $object_build;
}
public function view(array $objects) {
$object_order = array_keys($objects);
$context = $this
->getContext();
$default_cache_info = $this
->getDefaultCacheInfo($context);
$this
->alter('default_cache_info', $default_cache_info, $context);
$cache_info_map = $this
->getCacheInfoMap($objects, $context, $default_cache_info);
$build = $this->cache
->getMultiple($cache_info_map);
$build += $this
->getPlaceholders($objects, $cache_info_map, $context);
$remaining = array_diff_key($objects, $build);
if (!empty($remaining)) {
$object_build = $this
->renderRecursive($remaining);
$this->cache
->setMultiple($object_build, $cache_info_map);
$build += $object_build;
}
$return = array();
foreach ($object_order as $id) {
if (!isset($build[$id])) {
continue;
}
$render = $build[$id];
unset($render['#weight']);
$cache_info = $cache_info_map[$id];
if (!$this->renderStack
->isRecursive()) {
$this->renderStack
->processPostRenderCache($render, $cache_info);
}
$storage = $this->renderStack
->addRecursionStorage($render);
if (isset($render['#markup']) && (variable_get('render_cache_debug_output', FALSE) || variable_get('render_cache_debug_output_' . $this
->getPluginId(), FALSE) || !empty($cache_info['render_cache_debug_output']))) {
$prefix = '<!-- START RENDER ID: ' . $id . ' CACHE INFO: ' . "\n" . print_r($cache_info, TRUE);
$cache_hit = !empty($cache_info_map[$id]['cid']) && !isset($object_build[$id]) ? 'YES' : 'NO';
$prefix .= "\nCACHE_HIT: {$cache_hit}\n";
$full_storage = $storage;
$attached = $this->renderStack
->collectAttached($render);
if ($attached) {
$full_storage['#attached'] = $attached;
}
$attached = print_r($storage, TRUE);
$prefix .= "\nATTACHED: " . print_r($full_storage, TRUE) . "\n";
$prefix .= "\nHOOKS:\n";
$hook_prefix = 'render_cache_' . $this
->getPluginId() . '_';
foreach (array(
'default_cache_info',
'cache_info',
'keys',
'tags',
'hash',
'validate',
) as $hook) {
$prefix .= '* hook_' . $hook_prefix . $hook . "_alter()\n";
}
$prefix .= '-->';
$suffix = '<!-- END RENDER: ' . $id . ' -->';
$render['#markup'] = "\n{$prefix}\n" . $render['#markup'] . "\n{$suffix}\n";
}
$return[$id] = $render;
}
if (!$this->renderStack
->isRecursive() && variable_get('render_cache_send_drupal_cache_tags', TRUE)) {
$storage = $this->renderStack
->getRecursionStorage();
if (!empty($storage['#cache']['tags'])) {
$header = implode(' ', $storage['#cache']['tags']);
drupal_add_http_header('X-Drupal-Cache-Tags', $header, TRUE);
}
}
return $return;
}
protected function isCacheable(array $default_cache_info, array $context) {
$ignore_request_method_check = $default_cache_info['render_cache_ignore_request_method_check'];
return isset($default_cache_info['granularity']) && variable_get('render_cache_enabled', TRUE) && variable_get('render_cache_' . $this
->getPluginId() . '_enabled', TRUE) && render_cache_call_is_cacheable(NULL, $ignore_request_method_check);
}
protected function getCacheContext($object, array $context) {
return $context;
}
protected function getCacheInfo($object, array $context) {
return array();
}
protected function getCacheKeys($object, array $context) {
return array(
'render_cache',
$this
->getPluginId(),
);
}
protected function getCacheHash($object, array $context) {
return array(
'id' => $context['id'],
);
}
protected function getCacheTags($object, array $context) {
return array(
'rendered',
$this
->getPluginId() . '_view',
);
}
protected function getCacheValidate($object, array $context) {
return array();
}
protected function getDefaultCacheInfo($context) {
return array(
'bin' => 'cache_render',
'expire' => RenderCache::CACHE_PERMANENT,
'granularity' => DRUPAL_CACHE_PER_ROLE,
'keys' => array(),
'tags' => array(),
'max-age' => array(),
'downstream-ttl' => array(),
'render_strategy' => array(),
'hash' => array(),
'validate' => array(),
'render_cache_render_to_markup' => FALSE,
'render_cache_ignore_request_method_check' => FALSE,
'render_cache_cache_strategy' => NULL,
'render_cache_preserve_properties' => array(),
'render_cache_preserve_original' => FALSE,
);
}
protected function getCacheIdInfo($object, array $cache_info = array(), array $context = array()) {
$context = $this
->getCacheContext($object, $context);
$cache_info = drupal_array_merge_deep($cache_info, $this
->getCacheInfo($object, $context));
$cache_info += array(
'keys' => array(),
'hash' => array(),
'tags' => array(),
'validate' => array(),
);
$cache_info['keys'] = array_merge($cache_info['keys'], $this
->getCacheKeys($object, $context));
$cache_info['hash'] = array_merge($cache_info['hash'], $this
->getCacheHash($object, $context));
$cache_info['tags'] = Cache::mergeTags($cache_info['tags'], $this
->getCacheTags($object, $context));
$cache_info['validate'] = drupal_array_merge_deep($cache_info['validate'], $this
->getCacheValidate($object, $context));
$cache_info['hash']['render_method'] = !empty($cache_info['render_cache_render_to_markup']);
if ($cache_info['hash']['render_method']) {
$cache_info['hash']['render_options'] = serialize($cache_info['render_cache_render_to_markup']);
}
$this
->alter('cache_info', $cache_info, $object, $context);
if ($cache_info['granularity'] == DRUPAL_NO_CACHE) {
$cache_info['cid'] = NULL;
return $cache_info;
}
if (isset($cache_info['cid'])) {
return $cache_info;
}
$keys =& $cache_info['keys'];
$hash =& $cache_info['hash'];
$tags =& $cache_info['tags'];
$validate =& $cache_info['validate'];
$this
->alter('keys', $keys, $object, $cache_info, $context);
$this
->alter('hash', $hash, $object, $cache_info, $context);
$this
->alter('tags', $tags, $object, $cache_info, $context);
$this
->alter('validate', $validate, $object, $cache_info, $context);
$granularity = isset($cache_info['granularity']) ? $cache_info['granularity'] : NULL;
$cid_parts = array_merge($cache_info['keys'], drupal_render_cid_parts($granularity));
$algorithm = variable_get('render_cache_hash_algorithm', 'md5');
$cid_parts[] = hash($algorithm, implode('-', $cache_info['hash']));
$this
->alter('cid', $cid_parts, $cache_info, $object, $context);
$cache_info['cid'] = implode(':', $cid_parts);
if (!empty($cache_info['render_strategy'])) {
$cache_info['placeholder_id'] = $cache_info['cid'];
$cache_info['cid'] = NULL;
}
if ($cache_info['granularity'] == DRUPAL_CACHE_CUSTOM) {
$cache_info['cid'] = NULL;
}
if (empty($cache_info['render_cache_cache_strategy'])) {
$strategy = $this
->determineCachingStrategy($cache_info);
$cache_info['render_cache_cache_strategy'] = $strategy;
}
if (!empty($cache_info['render_cache_render_to_markup']['preserve properties'])) {
$cache_info['render_cache_preserve_properties'] = $cache_info['render_cache_render_to_markup']['preserve properties'];
}
unset($cache_info['render_cache_render_to_markup']);
return $cache_info;
}
protected function getCacheInfoMap(array $objects, array $context, array $default_cache_info) {
$cache_info_map = array();
$is_cacheable = $this
->isCacheable($default_cache_info, $context);
foreach ($objects as $id => $object) {
$object_context = $context;
$object_context['id'] = $id;
$cache_info_map[$id] = $this
->getCacheIdInfo($object, $default_cache_info, $object_context);
if (!$is_cacheable) {
$cache_info_map[$id]['cid'] = NULL;
}
}
return $cache_info_map;
}
protected function determineCachingStrategy($cache_info) {
if (empty($cache_info['render_cache_render_to_markup'])) {
return RenderCache::RENDER_CACHE_STRATEGY_NO_RENDER;
}
if (!empty($cache_info['render_cache_render_to_markup']['cache late'])) {
return RenderCache::RENDER_CACHE_STRATEGY_LATE_RENDER;
}
return RenderCache::RENDER_CACHE_STRATEGY_DIRECT_RENDER;
}
protected function renderRecursive(array $objects) {
$build = array();
foreach ($objects as $id => $object) {
$single_objects = array(
$id => $object,
);
$this->renderStack
->increaseRecursion();
$render = $this
->render($single_objects);
$storage = $this->renderStack
->decreaseRecursion();
if (!empty($render[$id])) {
$build[$id] = $render[$id];
$build[$id]['x_render_cache_recursion_storage'] = $storage;
}
}
return $build;
}
protected function alter($type, &$data, &$context1 = NULL, &$context2 = NULL, &$context3 = NULL) {
drupal_alter('render_cache_' . $this
->getPluginId() . '_' . $type, $data, $context1, $context2, $context3);
}
protected function getPlaceholders(array $objects, array $cache_info_map, $context) {
$build = array();
foreach ($cache_info_map as $id => $cache_info) {
if (empty($cache_info['placeholder_id'])) {
continue;
}
$ph_object = array(
'id' => $id,
'type' => $this
->getPluginId(),
'context' => $context,
'object' => $objects[$id],
'cache_info' => $cache_info,
'render_strategy' => $cache_info['render_strategy'],
);
$build[$id] = RenderCachePlaceholder::getPlaceholder(get_class($this) . '::renderPlaceholders', $ph_object, TRUE);
}
return $build;
}
public static function renderPlaceholders(array $args) {
$all_placeholders = array();
$strategies = array();
foreach ($args as $placeholder => $ph_object) {
foreach ($ph_object['render_strategy'] as $render_strategy) {
$strategies[$render_strategy][$placeholder] = $placeholder;
}
$strategies['direct'][$placeholder] = $placeholder;
}
foreach ($strategies as $render_strategy => $placeholder_keys) {
$rcs = render_cache_get_renderer($render_strategy);
if (!$rcs) {
continue;
}
$objects = array_intersect_key($args, $placeholder_keys);
if (empty($objects)) {
continue;
}
$placeholders = $rcs
->render($objects);
foreach ($placeholders as $placeholder => $render) {
$all_placeholders[$placeholder] = $render;
unset($args[$placeholder]);
}
}
return $all_placeholders;
}
}