Issue #2551989 by Wim Leers, Crell: Move replacing of placeholders from HtmlRenderer to HtmlResponseAttachmentsProcessor
parent
1234fa6190
commit
0549d59349
|
@ -943,7 +943,7 @@ services:
|
|||
- { name: event_subscriber }
|
||||
main_content_renderer.html:
|
||||
class: Drupal\Core\Render\MainContent\HtmlRenderer
|
||||
arguments: ['@title_resolver', '@plugin.manager.display_variant', '@event_dispatcher', '@module_handler', '@renderer', '@render_cache']
|
||||
arguments: ['@title_resolver', '@plugin.manager.display_variant', '@event_dispatcher', '@module_handler', '@renderer', '@render_cache', '%renderer.config%']
|
||||
tags:
|
||||
- { name: render.main_content_renderer, format: html }
|
||||
main_content_renderer.ajax:
|
||||
|
|
|
@ -1294,7 +1294,7 @@ function template_preprocess_html(&$variables) {
|
|||
'@token' => $token,
|
||||
]);
|
||||
$variables[$type]['#markup'] = $placeholder;
|
||||
$variables[$type]['#attached']['html_response_placeholders'][$type] = $placeholder;
|
||||
$variables[$type]['#attached']['html_response_attachment_placeholders'][$type] = $placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,12 +36,16 @@ class HtmlResponse extends Response implements CacheableResponseInterface, Attac
|
|||
// A render array can automatically be converted to a string and set the
|
||||
// necessary metadata.
|
||||
if (is_array($content) && (isset($content['#markup']))) {
|
||||
$content += ['#attached' => ['html_response_placeholders' => []]];
|
||||
$content += ['#attached' => [
|
||||
'html_response_attachment_placeholders' => [],
|
||||
'placeholders' => []],
|
||||
];
|
||||
$this->addCacheableDependency(CacheableMetadata::createFromRenderArray($content));
|
||||
$this->setAttachments($content['#attached']);
|
||||
$content = $content['#markup'];
|
||||
}
|
||||
|
||||
parent::setContent($content);
|
||||
return parent::setContent($content);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -99,25 +99,31 @@ class HtmlResponseAttachmentsProcessor implements AttachmentsResponseProcessorIn
|
|||
throw new \InvalidArgumentException('\Drupal\Core\Render\HtmlResponse instance expected.');
|
||||
}
|
||||
|
||||
// First, render the actual placeholders; this may cause additional
|
||||
// attachments to be added to the response, which the attachment
|
||||
// placeholders rendered by renderHtmlResponseAttachmentPlaceholders() will
|
||||
// need to include.
|
||||
$response = $this->renderPlaceholders($response);
|
||||
|
||||
$attached = $response->getAttachments();
|
||||
|
||||
// Get the placeholders from attached and then remove them.
|
||||
$placeholders = $attached['html_response_placeholders'];
|
||||
unset($attached['html_response_placeholders']);
|
||||
$attachment_placeholders = $attached['html_response_attachment_placeholders'];
|
||||
unset($attached['html_response_attachment_placeholders']);
|
||||
|
||||
$variables = $this->processAssetLibraries($attached, $placeholders);
|
||||
$variables = $this->processAssetLibraries($attached, $attachment_placeholders);
|
||||
|
||||
// Handle all non-asset attachments. This populates drupal_get_html_head().
|
||||
$all_attached = ['#attached' => $attached];
|
||||
drupal_process_attached($all_attached);
|
||||
|
||||
// Get HTML head elements - if present.
|
||||
if (isset($placeholders['head'])) {
|
||||
if (isset($attachment_placeholders['head'])) {
|
||||
$variables['head'] = drupal_get_html_head(FALSE);
|
||||
}
|
||||
|
||||
// Now replace the placeholders in the response content with the real data.
|
||||
$this->renderPlaceholders($response, $placeholders, $variables);
|
||||
// Now replace the attachment placeholders.
|
||||
$this->renderHtmlResponseAttachmentPlaceholders($response, $attachment_placeholders, $variables);
|
||||
|
||||
// Finally set the headers on the response if any bubbled.
|
||||
if (!empty($attached['http_header'])) {
|
||||
|
@ -127,6 +133,55 @@ class HtmlResponseAttachmentsProcessor implements AttachmentsResponseProcessorIn
|
|||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders placeholders (#attached['placeholders']).
|
||||
*
|
||||
* First, the HTML response object is converted to an equivalent render array,
|
||||
* with #markup being set to the response's content and #attached being set to
|
||||
* the response's attachments. Among these attachments, there may be
|
||||
* placeholders that need to be rendered (replaced).
|
||||
*
|
||||
* Next, RendererInterface::renderRoot() is called, which renders the
|
||||
* placeholders into their final markup.
|
||||
*
|
||||
* The markup that results from RendererInterface::renderRoot() is now the
|
||||
* original HTML response's content, but with the placeholders rendered. We
|
||||
* overwrite the existing content in the original HTML response object with
|
||||
* this markup. The markup that was rendered for the placeholders may also
|
||||
* have attachments (e.g. for CSS/JS assets) itself, and cacheability metadata
|
||||
* that indicates what that markup depends on. That metadata is also added to
|
||||
* the HTML response object.
|
||||
*
|
||||
* @param \Drupal\Core\Render\HtmlResponse $response
|
||||
* The HTML response whose placeholders are being replaced.
|
||||
*
|
||||
* @return \Drupal\Core\Render\HtmlResponse
|
||||
* The updated HTML response, with replaced placeholders.
|
||||
*
|
||||
* @see \Drupal\Core\Render\Renderer::replacePlaceholders()
|
||||
* @see \Drupal\Core\Render\Renderer::renderPlaceholder()
|
||||
*/
|
||||
protected function renderPlaceholders(HtmlResponse $response) {
|
||||
$build = [
|
||||
'#markup' => SafeString::create($response->getContent()),
|
||||
'#attached' => $response->getAttachments(),
|
||||
];
|
||||
// RendererInterface::renderRoot() renders the $build render array and
|
||||
// updates it in place. We don't care about the return value (which is just
|
||||
// $build['#markup']), but about the resulting render array.
|
||||
// @todo Simplify this when https://www.drupal.org/node/2495001 lands.
|
||||
$this->renderer->renderRoot($build);
|
||||
|
||||
// Update the Response object now that the placeholders have been rendered.
|
||||
$placeholders_bubbleable_metadata = BubbleableMetadata::createFromRenderArray($build);
|
||||
$response
|
||||
->setContent($build['#markup'])
|
||||
->addCacheableDependency($placeholders_bubbleable_metadata)
|
||||
->setAttachments($placeholders_bubbleable_metadata->getAttachments());
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes asset libraries into render arrays.
|
||||
*
|
||||
|
@ -174,8 +229,7 @@ class HtmlResponseAttachmentsProcessor implements AttachmentsResponseProcessorIn
|
|||
}
|
||||
|
||||
/**
|
||||
* Renders variables into HTML markup and replaces placeholders in the
|
||||
* response content.
|
||||
* Renders HTML response attachment placeholders.
|
||||
*
|
||||
* @param \Drupal\Core\Render\HtmlResponse $response
|
||||
* The HTML response to update.
|
||||
|
@ -186,7 +240,7 @@ class HtmlResponseAttachmentsProcessor implements AttachmentsResponseProcessorIn
|
|||
* The variables to render and replace, keyed by type with renderable
|
||||
* arrays as values.
|
||||
*/
|
||||
protected function renderPlaceholders(HtmlResponse $response, array $placeholders, array $variables) {
|
||||
protected function renderHtmlResponseAttachmentPlaceholders(HtmlResponse $response, array $placeholders, array $variables) {
|
||||
$content = $response->getContent();
|
||||
foreach ($placeholders as $type => $placeholder) {
|
||||
if (isset($variables[$type])) {
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
namespace Drupal\Core\Render\MainContent;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Controller\TitleResolverInterface;
|
||||
use Drupal\Core\Display\PageVariantInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\Render\HtmlResponse;
|
||||
use Drupal\Core\Render\PageDisplayVariantSelectionEvent;
|
||||
use Drupal\Core\Render\RenderCacheInterface;
|
||||
|
@ -74,6 +76,15 @@ class HtmlRenderer implements MainContentRendererInterface {
|
|||
*/
|
||||
protected $renderCache;
|
||||
|
||||
/**
|
||||
* The renderer configuration array.
|
||||
*
|
||||
* @see sites/default/default.services.yml
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $rendererConfig;
|
||||
|
||||
/**
|
||||
* Constructs a new HtmlRenderer.
|
||||
*
|
||||
|
@ -89,14 +100,17 @@ class HtmlRenderer implements MainContentRendererInterface {
|
|||
* The renderer service.
|
||||
* @param \Drupal\Core\Render\RenderCacheInterface $render_cache
|
||||
* The render cache service.
|
||||
* @param array $renderer_config
|
||||
* The renderer configuration array.
|
||||
*/
|
||||
public function __construct(TitleResolverInterface $title_resolver, PluginManagerInterface $display_variant_manager, EventDispatcherInterface $event_dispatcher, ModuleHandlerInterface $module_handler, RendererInterface $renderer, RenderCacheInterface $render_cache) {
|
||||
public function __construct(TitleResolverInterface $title_resolver, PluginManagerInterface $display_variant_manager, EventDispatcherInterface $event_dispatcher, ModuleHandlerInterface $module_handler, RendererInterface $renderer, RenderCacheInterface $render_cache, array $renderer_config) {
|
||||
$this->titleResolver = $title_resolver;
|
||||
$this->displayVariantManager = $display_variant_manager;
|
||||
$this->eventDispatcher = $event_dispatcher;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->renderer = $renderer;
|
||||
$this->renderCache = $render_cache;
|
||||
$this->rendererConfig = $renderer_config;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -125,11 +139,29 @@ class HtmlRenderer implements MainContentRendererInterface {
|
|||
// page.html.twig, hence add them here, just before rendering html.html.twig.
|
||||
$this->buildPageTopAndBottom($html);
|
||||
|
||||
// @todo https://www.drupal.org/node/2495001 Make renderRoot return a
|
||||
// cacheable render array directly.
|
||||
$this->renderer->renderRoot($html);
|
||||
// Render, but don't replace placeholders yet, because that happens later in
|
||||
// the render pipeline. To not replace placeholders yet, we use
|
||||
// RendererInterface::render() instead of RendererInterface::renderRoot().
|
||||
// @see \Drupal\Core\Render\HtmlResponseAttachmentsProcessor.
|
||||
$render_context = new RenderContext();
|
||||
$this->renderer->executeInRenderContext($render_context, function() use (&$html) {
|
||||
// RendererInterface::render() renders the $html render array and updates
|
||||
// it in place. We don't care about the return value (which is just
|
||||
// $html['#markup']), but about the resulting render array.
|
||||
// @todo Simplify this when https://www.drupal.org/node/2495001 lands.
|
||||
$this->renderer->render($html);
|
||||
});
|
||||
// RendererInterface::render() always causes bubbleable metadata to be
|
||||
// stored in the render context, no need to check it conditionally.
|
||||
$bubbleable_metadata = $render_context->pop();
|
||||
$bubbleable_metadata->applyTo($html);
|
||||
$content = $this->renderCache->getCacheableRenderArray($html);
|
||||
|
||||
// Also associate the required cache contexts.
|
||||
// (Because we use ::render() above and not ::renderRoot(), we manually must
|
||||
// ensure the HTML response varies by the required cache contexts.)
|
||||
$content['#cache']['contexts'] = Cache::mergeContexts($content['#cache']['contexts'], $this->rendererConfig['required_cache_contexts']);
|
||||
|
||||
// Also associate the "rendered" cache tag. This allows us to invalidate the
|
||||
// entire render cache, regardless of the cache bin.
|
||||
$content['#cache']['tags'][] = 'rendered';
|
||||
|
|
Loading…
Reference in New Issue