function Renderer::executeInRenderContext

Executes a callable within a render context.

Only for very advanced use cases. Prefer using ::renderRoot() and ::renderInIsolation() instead.

All rendering must happen within a render context. Within a render context, all bubbleable metadata is bubbled and hence tracked. Outside of a render context, it would be lost. This could lead to missing assets, incorrect cache variations (and thus security issues), insufficient cache invalidations, and so on.

Any and all rendering must therefore happen within a render context, and it is this method that provides that.

Parameters

\Drupal\Core\Render\RenderContext $context: The render context to execute the callable within.

callable $callable: The callable to execute.

Return value

mixed The callable's return value.

Overrides RendererInterface::executeInRenderContext

1 call to Renderer::executeInRenderContext()
Renderer::renderInIsolation in core/lib/Drupal/Core/Render/Renderer.php
Renders final HTML in situations where no assets are needed.

File

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

Class

Renderer
Turns a render array into an HTML string.

Namespace

Drupal\Core\Render

Code

public function executeInRenderContext(RenderContext $context, callable $callable) {
  // When executing in a render context, we need to isolate any bubbled
  // context within this method. To allow for async rendering, it's necessary
  // to detect if a fiber suspends within a render context. When this happens,
  // we swap the previous render context in before suspending upwards, then
  // back out again before resuming.
  $previous_context = $this->getCurrentRenderContext();
  // Set the provided context and call the callable, it will use that context.
  $this->setCurrentRenderContext($context);
  $fiber = new \Fiber(static fn() => $callable());
  $fiber->start();
  $resume_type = NULL;
  while (!$fiber->isTerminated()) {
    if ($fiber->isSuspended()) {
      // When ::executeInRenderContext() is executed within a Fiber, which is
      // always the case when rendering placeholders, if the callback results
      // in this fiber being suspended, we need to suspend again up to the
      // parent Fiber. Doing so allows other placeholders to be rendered
      // before returning here.
      if (\Fiber::getCurrent() !== NULL) {
        $this->setCurrentRenderContext($previous_context);
        \Fiber::suspend();
        $this->setCurrentRenderContext($context);
      }
      $resume_type = $fiber->resume();
    }
    // If the fiber has been suspended and has not signaled that it can be
    // immediately resumed, assume that the fiber is waiting on an async
    // operation and wait a bit.
    if (!$fiber->isTerminated() && $resume_type !== FiberResumeType::Immediate) {
      usleep(500);
    }
  }
  $result = $fiber->getReturn();
  assert($context->count() <= 1, 'Bubbling failed.');
  // Restore the original render context.
  $this->setCurrentRenderContext($previous_context);
  return $result;
}

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