Chris@0: — this contains BigPipe
Chris@0: * placeholders for the personalized parts of the page. Hence this sends the
Chris@0: * non-personalized parts of the page. Let's call it The Skeleton.
Chris@0: * 2. N chunks: a ';
Chris@0:
Chris@0: /**
Chris@0: * The BigPipe placeholder replacements stop signal.
Chris@0: *
Chris@0: * @var string
Chris@0: */
Chris@0: const STOP_SIGNAL = '';
Chris@0:
Chris@0: /**
Chris@0: * The renderer.
Chris@0: *
Chris@0: * @var \Drupal\Core\Render\RendererInterface
Chris@0: */
Chris@0: protected $renderer;
Chris@0:
Chris@0: /**
Chris@0: * The session.
Chris@0: *
Chris@0: * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
Chris@0: */
Chris@0: protected $session;
Chris@0:
Chris@0: /**
Chris@0: * The request stack.
Chris@0: *
Chris@0: * @var \Symfony\Component\HttpFoundation\RequestStack
Chris@0: */
Chris@0: protected $requestStack;
Chris@0:
Chris@0: /**
Chris@0: * The HTTP kernel.
Chris@0: *
Chris@0: * @var \Symfony\Component\HttpKernel\HttpKernelInterface
Chris@0: */
Chris@0: protected $httpKernel;
Chris@0:
Chris@0: /**
Chris@0: * The event dispatcher.
Chris@0: *
Chris@0: * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
Chris@0: */
Chris@0: protected $eventDispatcher;
Chris@0:
Chris@0: /**
Chris@0: * The config factory.
Chris@0: *
Chris@0: * @var \Drupal\Core\Config\ConfigFactoryInterface
Chris@0: */
Chris@0: protected $configFactory;
Chris@0:
Chris@0: /**
Chris@0: * Constructs a new BigPipe class.
Chris@0: *
Chris@0: * @param \Drupal\Core\Render\RendererInterface $renderer
Chris@0: * The renderer.
Chris@0: * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
Chris@0: * The session.
Chris@0: * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
Chris@0: * The request stack.
Chris@0: * @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
Chris@0: * The HTTP kernel.
Chris@0: * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
Chris@0: * The event dispatcher.
Chris@0: * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
Chris@0: * The config factory.
Chris@0: */
Chris@0: public function __construct(RendererInterface $renderer, SessionInterface $session, RequestStack $request_stack, HttpKernelInterface $http_kernel, EventDispatcherInterface $event_dispatcher, ConfigFactoryInterface $config_factory) {
Chris@0: $this->renderer = $renderer;
Chris@0: $this->session = $session;
Chris@0: $this->requestStack = $request_stack;
Chris@0: $this->httpKernel = $http_kernel;
Chris@0: $this->eventDispatcher = $event_dispatcher;
Chris@0: $this->configFactory = $config_factory;
Chris@0: }
Chris@0:
Chris@0: /**
Chris@0: * Performs tasks before sending content (and rendering placeholders).
Chris@0: */
Chris@0: protected function performPreSendTasks() {
Chris@0: // The content in the placeholders may depend on the session, and by the
Chris@0: // time the response is sent (see index.php), the session is already
Chris@0: // closed. Reopen it for the duration that we are rendering placeholders.
Chris@0: $this->session->start();
Chris@0: }
Chris@0:
Chris@0: /**
Chris@0: * Performs tasks after sending content (and rendering placeholders).
Chris@0: */
Chris@0: protected function performPostSendTasks() {
Chris@0: // Close the session again.
Chris@0: $this->session->save();
Chris@0: }
Chris@0:
Chris@0: /**
Chris@0: * Sends a chunk.
Chris@0: *
Chris@0: * @param string|\Drupal\Core\Render\HtmlResponse $chunk
Chris@0: * The string or response to append. String if there's no cacheability
Chris@0: * metadata or attachments to merge.
Chris@0: */
Chris@0: protected function sendChunk($chunk) {
Chris@0: assert(is_string($chunk) || $chunk instanceof HtmlResponse);
Chris@0: if ($chunk instanceof HtmlResponse) {
Chris@0: print $chunk->getContent();
Chris@0: }
Chris@0: else {
Chris@0: print $chunk;
Chris@0: }
Chris@0: flush();
Chris@0: }
Chris@0:
Chris@0: /**
Chris@0: * Sends an HTML response in chunks using the BigPipe technique.
Chris@0: *
Chris@0: * @param \Drupal\big_pipe\Render\BigPipeResponse $response
Chris@0: * The BigPipe response to send.
Chris@0: *
Chris@0: * @internal
Chris@0: * This method should only be invoked by
Chris@0: * \Drupal\big_pipe\Render\BigPipeResponse, which is itself an internal
Chris@0: * class.
Chris@0: */
Chris@0: public function sendContent(BigPipeResponse $response) {
Chris@0: $content = $response->getContent();
Chris@0: $attachments = $response->getAttachments();
Chris@0:
Chris@0: // First, gather the BigPipe placeholders that must be replaced.
Chris@0: $placeholders = isset($attachments['big_pipe_placeholders']) ? $attachments['big_pipe_placeholders'] : [];
Chris@0: $nojs_placeholders = isset($attachments['big_pipe_nojs_placeholders']) ? $attachments['big_pipe_nojs_placeholders'] : [];
Chris@0:
Chris@0: // BigPipe sends responses using "Transfer-Encoding: chunked". To avoid
Chris@0: // sending already-sent assets, it is necessary to track cumulative assets
Chris@0: // from all previously rendered/sent chunks.
Chris@0: // @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.41
Chris@0: $cumulative_assets = AttachedAssets::createFromRenderArray(['#attached' => $attachments]);
Chris@0: $cumulative_assets->setAlreadyLoadedLibraries($attachments['library']);
Chris@0:
Chris@0: $this->performPreSendTasks();
Chris@0:
Chris@0: // Find the closing