protected function BigPipe::sendPlaceholders in Drupal 10
Same name and namespace in other branches
- 8 core/modules/big_pipe/src/Render/BigPipe.php \Drupal\big_pipe\Render\BigPipe::sendPlaceholders()
- 9 core/modules/big_pipe/src/Render/BigPipe.php \Drupal\big_pipe\Render\BigPipe::sendPlaceholders()
Sends BigPipe placeholders' replacements as embedded AJAX responses.
Parameters
array $placeholders: Associative array; the BigPipe placeholders. Keys are the BigPipe placeholder IDs.
array $placeholder_order: Indexed array; the order in which the BigPipe placeholders must be sent. Values are the BigPipe placeholder IDs. (These values correspond to keys in $placeholders.)
\Drupal\Core\Asset\AttachedAssetsInterface $cumulative_assets: The cumulative assets sent so far; to be updated while rendering BigPipe placeholders.
Throws
\Exception If an exception is thrown during the rendering of a placeholder, it is caught to allow the other placeholders to still be replaced. But when error logging is configured to be verbose, the exception is rethrown to simplify debugging.
File
- core/
modules/ big_pipe/ src/ Render/ BigPipe.php, line 520
Class
- BigPipe
- Service for sending an HTML response in chunks (to get faster page loads).
Namespace
Drupal\big_pipe\RenderCode
protected function sendPlaceholders(array $placeholders, array $placeholder_order, AttachedAssetsInterface $cumulative_assets) {
// Return early if there are no BigPipe placeholders to send.
if (empty($placeholders)) {
return;
}
// Send the start signal.
$this
->sendChunk("\n" . static::START_SIGNAL . "\n");
// A BigPipe response consists of an HTML response plus multiple embedded
// AJAX responses. To process the attachments of those AJAX responses, we
// need a fake request that is identical to the main request, but with
// one change: it must have the right Accept header, otherwise the work-
// around for a bug in IE9 will cause not JSON, but <textarea>-wrapped JSON
// to be returned.
// @see \Drupal\Core\EventSubscriber\AjaxResponseSubscriber::onResponse()
$fake_request = $this->requestStack
->getMainRequest()
->duplicate();
$fake_request->headers
->set('Accept', 'application/vnd.drupal-ajax');
foreach ($placeholder_order as $placeholder_id) {
if (!isset($placeholders[$placeholder_id])) {
continue;
}
// Render the placeholder.
$placeholder_render_array = $placeholders[$placeholder_id];
try {
$elements = $this
->renderPlaceholder($placeholder_id, $placeholder_render_array);
} catch (\Exception $e) {
if ($this->configFactory
->get('system.logging')
->get('error_level') === ERROR_REPORTING_DISPLAY_VERBOSE) {
throw $e;
}
else {
trigger_error($e, E_USER_ERROR);
continue;
}
}
// Create a new AjaxResponse.
$ajax_response = new AjaxResponse();
// JavaScript's querySelector automatically decodes HTML entities in
// attributes, so we must decode the entities of the current BigPipe
// placeholder ID (which has HTML entities encoded since we use it to find
// the placeholders).
$big_pipe_js_placeholder_id = Html::decodeEntities($placeholder_id);
$ajax_response
->addCommand(new ReplaceCommand(sprintf('[data-big-pipe-placeholder-id="%s"]', $big_pipe_js_placeholder_id), $elements['#markup']));
$ajax_response
->setAttachments($elements['#attached']);
// Push a fake request with the asset libraries loaded so far and dispatch
// KernelEvents::RESPONSE event. This results in the attachments for the
// AJAX response being processed by AjaxResponseAttachmentsProcessor and
// hence:
// - the necessary AJAX commands to load the necessary missing asset
// libraries and updated AJAX page state are added to the AJAX response
// - the attachments associated with the response are finalized, which
// allows us to track the total set of asset libraries sent in the
// initial HTML response plus all embedded AJAX responses sent so far.
$fake_request->request
->set('ajax_page_state', [
'libraries' => implode(',', $cumulative_assets
->getAlreadyLoadedLibraries()),
] + $cumulative_assets
->getSettings()['ajaxPageState']);
try {
$ajax_response = $this
->filterEmbeddedResponse($fake_request, $ajax_response);
} catch (\Exception $e) {
if ($this->configFactory
->get('system.logging')
->get('error_level') === ERROR_REPORTING_DISPLAY_VERBOSE) {
throw $e;
}
else {
trigger_error($e, E_USER_ERROR);
continue;
}
}
// Send this embedded AJAX response.
$json = $ajax_response
->getContent();
$output = <<<EOF
<script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="{<span class="php-variable">$placeholder_id</span>}">
{<span class="php-variable">$json</span>}
</script>
EOF;
$this
->sendChunk($output);
// Another placeholder was rendered and sent, track the set of asset
// libraries sent so far. Any new settings are already sent; we don't need
// to track those.
if (isset($ajax_response
->getAttachments()['drupalSettings']['ajaxPageState']['libraries'])) {
$cumulative_assets
->setAlreadyLoadedLibraries(explode(',', $ajax_response
->getAttachments()['drupalSettings']['ajaxPageState']['libraries']));
}
}
// Send the stop signal.
$this
->sendChunk("\n" . static::STOP_SIGNAL . "\n");
}