protected function BigPipe::sendNoJsPlaceholders in Drupal 10
Same name and namespace in other branches
- 8 core/modules/big_pipe/src/Render/BigPipe.php \Drupal\big_pipe\Render\BigPipe::sendNoJsPlaceholders()
- 9 core/modules/big_pipe/src/Render/BigPipe.php \Drupal\big_pipe\Render\BigPipe::sendNoJsPlaceholders()
Sends no-JS BigPipe placeholders' replacements as embedded HTML responses.
Parameters
string $html: HTML markup.
array $no_js_placeholders: Associative array; the no-JS BigPipe placeholders. Keys are the BigPipe selectors.
\Drupal\Core\Asset\AttachedAssetsInterface $cumulative_assets: The cumulative assets sent so far; to be updated while rendering no-JS 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 390
Class
- BigPipe
- Service for sending an HTML response in chunks (to get faster page loads).
Namespace
Drupal\big_pipe\RenderCode
protected function sendNoJsPlaceholders($html, $no_js_placeholders, AttachedAssetsInterface $cumulative_assets) {
// Split the HTML on every no-JS placeholder string.
$placeholder_strings = array_keys($no_js_placeholders);
$fragments = static::splitHtmlOnPlaceholders($html, $placeholder_strings);
// Determine how many occurrences there are of each no-JS placeholder.
$placeholder_occurrences = array_count_values(array_intersect($fragments, $placeholder_strings));
// Set up a variable to store the content of placeholders that have multiple
// occurrences.
$multi_occurrence_placeholders_content = [];
foreach ($fragments as $fragment) {
// If the fragment isn't one of the no-JS placeholders, it is the HTML in
// between placeholders and it must be printed & flushed immediately. The
// rest of the logic in the loop handles the placeholders.
if (!isset($no_js_placeholders[$fragment])) {
$this
->sendChunk($fragment);
continue;
}
// If there are multiple occurrences of this particular placeholder, and
// this is the second occurrence, we can skip all calculations and just
// send the same content.
if ($placeholder_occurrences[$fragment] > 1 && isset($multi_occurrence_placeholders_content[$fragment])) {
$this
->sendChunk($multi_occurrence_placeholders_content[$fragment]);
continue;
}
$placeholder = $fragment;
assert(isset($no_js_placeholders[$placeholder]));
$token = Crypt::randomBytesBase64(55);
// Render the placeholder, but include the cumulative settings assets, so
// we can calculate the overall settings for the entire page.
$placeholder_plus_cumulative_settings = [
'placeholder' => $no_js_placeholders[$placeholder],
'cumulative_settings_' . $token => [
'#attached' => [
'drupalSettings' => $cumulative_assets
->getSettings(),
],
],
];
try {
$elements = $this
->renderPlaceholder($placeholder, $placeholder_plus_cumulative_settings);
} 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 HtmlResponse. Ensure the CSS and (non-bottom) JS is sent
// before the HTML they're associated with. In other words: ensure the
// critical assets for this placeholder's markup are loaded first.
// @see \Drupal\Core\Render\HtmlResponseSubscriber
// @see template_preprocess_html()
$css_placeholder = '<nojs-bigpipe-placeholder-styles-placeholder token="' . $token . '">';
$js_placeholder = '<nojs-bigpipe-placeholder-scripts-placeholder token="' . $token . '">';
$elements['#markup'] = BigPipeMarkup::create($css_placeholder . $js_placeholder . (string) $elements['#markup']);
$elements['#attached']['html_response_attachment_placeholders']['styles'] = $css_placeholder;
$elements['#attached']['html_response_attachment_placeholders']['scripts'] = $js_placeholder;
$html_response = new HtmlResponse();
$html_response
->setContent($elements);
$html_response
->getCacheableMetadata()
->setCacheMaxAge(0);
// Push a fake request with the asset libraries loaded so far and dispatch
// KernelEvents::RESPONSE event. This results in the attachments for the
// HTML response being processed by HtmlResponseAttachmentsProcessor and
// hence:
// - the HTML to load the CSS can be rendered.
// - the HTML to load the JS (at the top) can be rendered.
$fake_request = $this->requestStack
->getMainRequest()
->duplicate();
$fake_request->request
->set('ajax_page_state', [
'libraries' => implode(',', $cumulative_assets
->getAlreadyLoadedLibraries()),
]);
try {
$html_response = $this
->filterEmbeddedResponse($fake_request, $html_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 HTML response.
$this
->sendChunk($html_response);
// Another placeholder was rendered and sent, track the set of asset
// libraries sent so far. Any new settings also need to be tracked, so
// they can be sent in ::sendPreBody().
$cumulative_assets
->setAlreadyLoadedLibraries(array_merge($cumulative_assets
->getAlreadyLoadedLibraries(), $html_response
->getAttachments()['library']));
$cumulative_assets
->setSettings($html_response
->getAttachments()['drupalSettings']);
// If there are multiple occurrences of this particular placeholder, track
// the content that was sent, so we can skip all calculations for the next
// occurrence.
if ($placeholder_occurrences[$fragment] > 1) {
$multi_occurrence_placeholders_content[$fragment] = $html_response
->getContent();
}
}
}