You are here

protected function EarlyRenderingControllerWrapperSubscriber::wrapControllerExecutionInRenderContext in Drupal 9

Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php \Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber::wrapControllerExecutionInRenderContext()

Wraps a controller execution in a render context.

Parameters

callable $controller: The controller to execute.

array $arguments: The arguments to pass to the controller.

Return value

mixed The return value of the controller.

Throws

\LogicException When early rendering has occurred in a controller that returned a Response or domain object that cares about attachments or cacheability.

See also

\Symfony\Component\HttpKernel\HttpKernel::handleRaw()

1 call to EarlyRenderingControllerWrapperSubscriber::wrapControllerExecutionInRenderContext()
EarlyRenderingControllerWrapperSubscriber::onController in core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php
Ensures bubbleable metadata from early rendering is not lost.

File

core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php, line 118

Class

EarlyRenderingControllerWrapperSubscriber
Subscriber that wraps controllers, to handle early rendering.

Namespace

Drupal\Core\EventSubscriber

Code

protected function wrapControllerExecutionInRenderContext($controller, array $arguments) {
  $context = new RenderContext();
  $response = $this->renderer
    ->executeInRenderContext($context, function () use ($controller, $arguments) {

    // Now call the actual controller, just like HttpKernel does.
    return call_user_func_array($controller, $arguments);
  });

  // If early rendering happened, i.e. if code in the controller called
  // drupal_render() outside of a render context, then the bubbleable metadata
  // for that is stored in the current render context.
  if (!$context
    ->isEmpty()) {

    /** @var \Drupal\Core\Render\BubbleableMetadata $early_rendering_bubbleable_metadata */
    $early_rendering_bubbleable_metadata = $context
      ->pop();

    // If a render array or AjaxResponse is returned by the controller, merge
    // the "lost" bubbleable metadata.
    if (is_array($response)) {
      BubbleableMetadata::createFromRenderArray($response)
        ->merge($early_rendering_bubbleable_metadata)
        ->applyTo($response);
    }
    elseif ($response instanceof AjaxResponse) {
      $response
        ->addAttachments($early_rendering_bubbleable_metadata
        ->getAttachments());

      // @todo Make AjaxResponse cacheable in
      //   https://www.drupal.org/node/956186. Meanwhile, allow contrib
      //   subclasses to be.
      if ($response instanceof CacheableResponseInterface) {
        $response
          ->addCacheableDependency($early_rendering_bubbleable_metadata);
      }
    }
    elseif ($response instanceof AttachmentsInterface || $response instanceof CacheableResponseInterface || $response instanceof CacheableDependencyInterface) {
      throw new \LogicException(sprintf('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: %s.', get_class($response)));
    }
    else {

      // A Response or domain object is returned that does not care about
      // attachments nor cacheability; for instance, a RedirectResponse. It is
      // safe to discard any early rendering metadata.
    }
  }
  return $response;
}