function Renderer::replacePlaceholders

Same name in other branches
  1. 9 core/lib/Drupal/Core/Render/Renderer.php \Drupal\Core\Render\Renderer::replacePlaceholders()
  2. 8.9.x core/lib/Drupal/Core/Render/Renderer.php \Drupal\Core\Render\Renderer::replacePlaceholders()
  3. 11.x core/lib/Drupal/Core/Render/Renderer.php \Drupal\Core\Render\Renderer::replacePlaceholders()

Replaces placeholders.

Placeholders may have:

  • #lazy_builder callback, to build a render array to be rendered into markup that can replace the placeholder
  • #cache: to cache the result of the placeholder

Also merges the bubbleable metadata resulting from the rendering of the contents of the placeholders. Hence $elements will be contain the entirety of bubbleable metadata.

Parameters

array &$elements: The structured array describing the data being rendered. Including the bubbleable metadata associated with the markup that replaced the placeholders.

Return value

bool Whether placeholders were replaced.

See also

\Drupal\Core\Render\Renderer::renderPlaceholder()

1 call to Renderer::replacePlaceholders()
Renderer::doRender in core/lib/Drupal/Core/Render/Renderer.php
See the docs for ::render().

File

core/lib/Drupal/Core/Render/Renderer.php, line 695

Class

Renderer
Turns a render array into an HTML string.

Namespace

Drupal\Core\Render

Code

protected function replacePlaceholders(array &$elements) {
    if (!isset($elements['#attached']['placeholders']) || empty($elements['#attached']['placeholders'])) {
        return FALSE;
    }
    // The 'status messages' placeholder needs to be special cased, because it
    // depends on global state that can be modified when other placeholders are
    // being rendered: any code can add messages to render.
    // This violates the principle that each lazy builder must be able to render
    // itself in isolation, and therefore in any order. However, we cannot
    // change the way \Drupal\Core\Messenger\Messenger works in the Drupal 8
    // cycle. So we have to accommodate its special needs.
    // Allowing placeholders to be rendered in a particular order (in this case:
    // last) would violate this isolation principle. Thus a monopoly is granted
    // to this one special case, with this hard-coded solution.
    // @see \Drupal\Core\Render\Element\StatusMessages
    // @see https://www.drupal.org/node/2712935#comment-11368923
    // First render all placeholders except 'status messages' placeholders.
    $message_placeholders = [];
    $fibers = [];
    foreach ($elements['#attached']['placeholders'] as $placeholder => $placeholder_element) {
        if (isset($placeholder_element['#lazy_builder']) && $placeholder_element['#lazy_builder'][0] === 'Drupal\\Core\\Render\\Element\\StatusMessages::renderMessages') {
            $message_placeholders[] = $placeholder;
        }
        else {
            // Get the render array for the given placeholder
            $fibers[$placeholder] = new \Fiber(function () use ($placeholder_element) {
                return [
                    $this->doRenderPlaceholder($placeholder_element),
                    $placeholder_element,
                ];
            });
        }
    }
    $iterations = 0;
    while (count($fibers) > 0) {
        foreach ($fibers as $placeholder => $fiber) {
            if (!$fiber->isStarted()) {
                $fiber->start();
            }
            elseif ($fiber->isSuspended()) {
                $fiber->resume();
            }
            // If the Fiber hasn't terminated by this point, move onto the next
            // placeholder, we'll resume this fiber again when we get back here.
            if (!$fiber->isTerminated()) {
                // If we've gone through the placeholders once already, and they're
                // still not finished, then start to allow code higher up the stack to
                // get on with something else.
                if ($iterations) {
                    $fiber = \Fiber::getCurrent();
                    if ($fiber !== NULL) {
                        $fiber->suspend();
                    }
                }
                continue;
            }
            [
                $markup,
                $placeholder_element,
            ] = $fiber->getReturn();
            $elements = $this->doReplacePlaceholder($placeholder, $markup, $elements, $placeholder_element);
            unset($fibers[$placeholder]);
        }
        $iterations++;
    }
    // Then render 'status messages' placeholders.
    foreach ($message_placeholders as $message_placeholder) {
        $elements = $this->renderPlaceholder($message_placeholder, $elements);
    }
    return TRUE;
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.