View source
<?php
namespace Drupal\render_cache\Cache;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\CacheableInterface;
use Drupal\Core\Render\Element;
use RenderCache;
class RenderStack implements RenderStackInterface, CacheableInterface {
public function getCacheKeys() {
return array(
'render_cache',
'foo',
);
}
public function getCacheTags() {
return array(
array(
'node' => 1,
),
);
}
public function getCacheMaxAge() {
return 600;
}
public function isCacheable() {
return TRUE;
}
protected $recursionLevel = 0;
protected $recursionStorage = array();
protected $supportsDynamicAssets = FALSE;
public function increaseRecursion() {
$this->recursionLevel += 1;
$this->recursionStorage[$this->recursionLevel] = array();
}
public function decreaseRecursion() {
$storage = $this
->getRecursionStorage();
unset($this->recursionStorage[$this->recursionLevel]);
$this->recursionLevel -= 1;
return $storage;
}
public function isRecursive() {
return $this->recursionLevel > 0;
}
public function getRecursionLevel() {
return $this->recursionLevel;
}
public function getRecursionStorage() {
if (!isset($this->recursionStorage[$this->recursionLevel])) {
$this->recursionStorage[$this->recursionLevel] = array();
}
$storage = $this->recursionStorage[$this->recursionLevel];
$render = array();
if (!empty($storage)) {
$render = $this
->collectAndRemoveAssets($storage);
$attached = $this
->collectAttached($storage);
if ($attached) {
$render['#attached'] = $attached;
}
$this->recursionStorage[$this->recursionLevel] = $render;
}
return $render;
}
public function setRecursionStorage(array $storage) {
$this->recursionStorage[$this->recursionLevel] = $storage;
}
public function addRecursionStorage(array &$render, $collect_attached = FALSE) {
$storage = $this
->collectAndRemoveAssets($render);
if ($collect_attached) {
$storage['#attached'] = $this
->collectAttached($render);
}
$this->recursionStorage[$this->recursionLevel][] = $storage;
return $storage;
}
public function drupalRender(array &$render) {
$markup = drupal_render($render);
$this
->addRecursionStorage($render);
return $markup;
}
public function collectAttached(array $render) {
return drupal_render_collect_attached($render, TRUE);
}
public function supportsDynamicAssets($supportsDynamicAssets = NULL) {
if (isset($supportsDynamicAssets)) {
$this->supportsDynamicAssets = $supportsDynamicAssets;
}
return $this->supportsDynamicAssets;
}
public function render(array &$render) {
$this
->increaseRecursion();
if (!empty($render['x_render_cache_recursion_storage'])) {
$storage = $render['x_render_cache_recursion_storage'];
unset($render['x_render_cache_recursion_storage']);
$this
->addRecursionStorage($storage, TRUE);
}
$markup = $this
->drupalRender($render);
if (!$this
->supportsDynamicAssets()) {
$storage = array();
$storage['#attached'] = $this
->collectAttached($render);
$this
->addRecursionStorage($storage, TRUE);
}
$storage = $this
->decreaseRecursion();
$original_render = $render;
$render = $storage;
$render['#markup'] =& $markup;
return array(
$markup,
$original_render,
);
}
public function collectAndRemoveAssets(&$element, $recursive = FALSE) {
$assets = $this
->collectAndRemoveD8Properties($element);
$assets['#cache']['tags'] = isset($assets['#cache']['tags']) ? $assets['#cache']['tags'] : array();
$assets['#cache']['max-age'] = isset($assets['#cache']['max-age']) ? $assets['#cache']['max-age'] : array();
$assets['#cache']['downstream-ttl'] = isset($assets['#cache']['downstream-ttl']) ? $assets['#cache']['downstream-ttl'] : array();
$assets['#post_render_cache'] = isset($assets['#post_render_cache']) ? $assets['#post_render_cache'] : array();
if (!is_array($assets['#cache']['max-age'])) {
$assets['#cache']['max-age'] = array(
$assets['#cache']['max-age'],
);
}
if (!is_array($assets['#cache']['downstream-ttl'])) {
$assets['#cache']['downstream-ttl'] = array(
$assets['#cache']['downstream-ttl'],
);
}
$children = Element::children($element, TRUE);
foreach ($children as $key) {
$new_assets = $this
->collectAndRemoveAssets($element[$key], TRUE);
$assets['#cache']['tags'] = Cache::mergeTags($assets['#cache']['tags'], $new_assets['#cache']['tags']);
$assets['#cache']['max-age'] = NestedArray::mergeDeep($assets['#cache']['max-age'], $new_assets['#cache']['max-age']);
$assets['#cache']['downstream-ttl'] = NestedArray::mergeDeep($assets['#cache']['downstream-ttl'], $new_assets['#cache']['downstream-ttl']);
$assets['#post_render_cache'] = NestedArray::mergeDeep($assets['#post_render_cache'], $new_assets['#post_render_cache']);
}
if (!$recursive) {
if (empty($assets['#cache']['tags'])) {
unset($assets['#cache']['tags']);
}
if (empty($assets['#cache']['max-age'])) {
unset($assets['#cache']['max-age']);
}
if (empty($assets['#cache']['downstream-ttl'])) {
unset($assets['#cache']['downstream-ttl']);
}
if (empty($assets['#cache'])) {
unset($assets['#cache']);
}
if (empty($assets['#post_render_cache'])) {
unset($assets['#post_render_cache']);
}
}
return $assets;
}
public function collectAndRemoveD8Properties(&$element) {
$render = array();
if (!empty($element['#cache']['tags'])) {
$render['#cache']['tags'] = $element['#cache']['tags'];
unset($element['#cache']['tags']);
}
if (!empty($element['#cache']['max-age'])) {
$render['#cache']['max-age'] = $element['#cache']['max-age'];
unset($element['#cache']['max-age']);
}
if (!empty($element['#cache']['downstream-ttl'])) {
$render['#cache']['downstream-ttl'] = $element['#cache']['downstream-ttl'];
unset($element['#cache']['downstream-ttl']);
}
if (empty($element['#cache'])) {
unset($element['#cache']);
}
if (!empty($element['#post_render_cache'])) {
$render['#post_render_cache'] = $element['#post_render_cache'];
unset($element['#post_render_cache']);
}
return $render;
}
public function convertRenderArrayToD7($render) {
$render['#attached']['render_cache'] = $this
->collectAndRemoveD8Properties($render);
return $render;
}
public function convertRenderArrayFromD7($render) {
if (!empty($render['#attached']['render_cache'])) {
$render += $render['#attached']['render_cache'];
unset($render['#attached']['render_cache']);
}
return $render;
}
public function processPostRenderCache(&$render, $cache_info) {
$strategy = $cache_info['render_cache_cache_strategy'];
if ($strategy != RenderCache::RENDER_CACHE_STRATEGY_DIRECT_RENDER) {
return;
}
$storage = $this
->collectAndRemoveAssets($render);
while (!empty($storage['#post_render_cache'])) {
$post_render_cache = $storage['#post_render_cache'];
unset($storage['#post_render_cache']);
$this
->increaseRecursion();
$this
->addRecursionStorage($storage);
foreach (array_keys($post_render_cache) as $callback) {
foreach ($post_render_cache[$callback] as $context) {
$render = call_user_func_array($callback, array(
$render,
$context,
));
}
}
$storage = $this
->collectAndRemoveAssets($render);
$this
->addRecursionStorage($storage);
$storage = $this
->decreaseRecursion();
if (!empty($storage['#attached'])) {
$render += array(
'#attached' => array(),
);
$render['#attached'] = NestedArray::mergeDeep($render['#attached'], $storage['#attached']);
unset($storage['#attached']);
}
}
$render = NestedArray::mergeDeep($render, $storage);
}
public function drupal_add_assets($type, $data = NULL, $options = NULL) {
if (isset($options)) {
if (!is_array($options)) {
$options = array(
'type' => $options,
);
}
}
else {
$options = array();
}
if (isset($data) && $this
->isRecursive()) {
$new_options = $options;
$new_options['data'] = $data;
$storage = array();
$storage['#attached'][$type][] = $new_options;
$this
->addRecursionStorage($storage, TRUE);
return;
}
return $this
->callOriginalFunction("drupal_add_{$type}", $data, $options);
}
public function drupal_add_library($module, $name, $every_page = NULL) {
if ($this
->isRecursive()) {
$storage = array();
$storage['#attached']['library'][] = array(
$module,
$name,
);
$this
->addRecursionStorage($storage, TRUE);
return TRUE;
}
return $this
->callOriginalFunction("drupal_add_library", $module, $name, $every_page);
}
public function drupal_process_attached($elements, $group = 0, $dependency_check = FALSE, $every_page = NULL) {
if ($this
->isRecursive()) {
$storage = array();
$storage['#attached'] = $elements['#attached'];
$this
->addRecursionStorage($storage, TRUE);
return TRUE;
}
return $this
->callOriginalFunction("drupal_process_attached", $elements, $group, $dependency_check, $every_page);
}
public function callOriginalFunction($function) {
global $conf;
$args = func_get_args();
array_shift($args);
$name = $function . "_function";
$old = $conf[$name];
unset($conf[$name]);
$return = call_user_func_array($function, $args);
$conf[$name] = $old;
return $return;
}
}