class EarlyRenderingControllerWrapperSubscriber implements EventSubscriberInterface (View source)

Subscriber that wraps controllers, to handle early rendering.

When controllers call RendererInterface::render() outside of a render context, we call that "early rendering". Controllers should return only render arrays, but we cannot prevent controllers from doing early rendering. The problem with early rendering is that the bubbleable metadata (cacheability & attachments) are lost.

This can lead to broken pages (missing assets), stale pages (missing cache tags causing a page not to be invalidated) or even security problems (missing cache contexts causing a cached page not to be varied sufficiently).

This event subscriber wraps all controller executions in a closure that sets up a render context. Consequently, any early rendering will have their bubbleable metadata (assets & cacheability) stored on that render context.

If the render context is empty, then the controller either did not do any rendering at all, or used the RendererInterface::renderRoot() or ::renderPlain() methods. In that case, no bubbleable metadata is lost.

If the render context is not empty, then the controller did use RendererInterface::render(), and bubbleable metadata was collected. This bubbleable metadata is then merged onto the render array.

In other words: this just exists to ease the transition to Drupal 8: it allows controllers that return render arrays (the majority) and \Drupal\Core\Ajax\AjaxResponse\AjaxResponse objects (a sizable minority that often involve a fair amount of rendering) to still do early rendering. But controllers that return any other kind of response are already expected to do the right thing, so if early rendering is detected in such a case, an exception is thrown.

Remove in Drupal 9.0.0, by disallowing early rendering.

Properties

protected ArgumentResolverInterface $argumentResolver

The argument resolver.

protected RendererInterface $renderer

The renderer.

Methods

__construct(ArgumentResolverInterface $argument_resolver, RendererInterface $renderer)

Constructs a new EarlyRenderingControllerWrapperSubscriber instance.

onController(ControllerEvent $event)

Ensures bubbleable metadata from early rendering is not lost.

mixed
wrapControllerExecutionInRenderContext(callable $controller, array $arguments)

Wraps a controller execution in a render context.

static 
getSubscribedEvents()

{@inheritdoc}

Details

__construct(ArgumentResolverInterface $argument_resolver, RendererInterface $renderer)

Constructs a new EarlyRenderingControllerWrapperSubscriber instance.

Parameters

ArgumentResolverInterface $argument_resolver

The argument resolver.

RendererInterface $renderer

The renderer.

onController(ControllerEvent $event)

Ensures bubbleable metadata from early rendering is not lost.

Parameters

ControllerEvent $event

The controller event.

protected mixed wrapControllerExecutionInRenderContext(callable $controller, array $arguments)

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.

Exceptions

LogicException

See also

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

static getSubscribedEvents()

{@inheritdoc}