Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\big_pipe\EventSubscriber;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Core\Render\HtmlResponse;
|
Chris@0
|
6 use Drupal\big_pipe\Render\BigPipe;
|
Chris@0
|
7 use Drupal\big_pipe\Render\BigPipeResponse;
|
Chris@0
|
8 use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
Chris@0
|
9 use Symfony\Component\HttpKernel\KernelEvents;
|
Chris@0
|
10 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
Chris@0
|
11
|
Chris@0
|
12 /**
|
Chris@0
|
13 * Response subscriber to replace the HtmlResponse with a BigPipeResponse.
|
Chris@0
|
14 *
|
Chris@0
|
15 * @see \Drupal\big_pipe\Render\BigPipe
|
Chris@0
|
16 *
|
Chris@0
|
17 * @todo Refactor once https://www.drupal.org/node/2577631 lands.
|
Chris@0
|
18 */
|
Chris@0
|
19 class HtmlResponseBigPipeSubscriber implements EventSubscriberInterface {
|
Chris@0
|
20
|
Chris@0
|
21 /**
|
Chris@0
|
22 * The BigPipe service.
|
Chris@0
|
23 *
|
Chris@0
|
24 * @var \Drupal\big_pipe\Render\BigPipe
|
Chris@0
|
25 */
|
Chris@0
|
26 protected $bigPipe;
|
Chris@0
|
27
|
Chris@0
|
28 /**
|
Chris@0
|
29 * Constructs a HtmlResponseBigPipeSubscriber object.
|
Chris@0
|
30 *
|
Chris@0
|
31 * @param \Drupal\big_pipe\Render\BigPipe $big_pipe
|
Chris@0
|
32 * The BigPipe service.
|
Chris@0
|
33 */
|
Chris@0
|
34 public function __construct(BigPipe $big_pipe) {
|
Chris@0
|
35 $this->bigPipe = $big_pipe;
|
Chris@0
|
36 }
|
Chris@0
|
37
|
Chris@0
|
38 /**
|
Chris@0
|
39 * Adds markers to the response necessary for the BigPipe render strategy.
|
Chris@0
|
40 *
|
Chris@0
|
41 * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
|
Chris@0
|
42 * The event to process.
|
Chris@0
|
43 */
|
Chris@0
|
44 public function onRespondEarly(FilterResponseEvent $event) {
|
Chris@0
|
45 $response = $event->getResponse();
|
Chris@0
|
46 if (!$response instanceof HtmlResponse) {
|
Chris@0
|
47 return;
|
Chris@0
|
48 }
|
Chris@0
|
49
|
Chris@0
|
50 // Wrap the scripts_bottom placeholder with a marker before and after,
|
Chris@0
|
51 // because \Drupal\big_pipe\Render\BigPipe needs to be able to extract that
|
Chris@0
|
52 // markup if there are no-JS BigPipe placeholders.
|
Chris@0
|
53 // @see \Drupal\big_pipe\Render\BigPipe::sendPreBody()
|
Chris@0
|
54 $attachments = $response->getAttachments();
|
Chris@0
|
55 if (isset($attachments['html_response_attachment_placeholders']['scripts_bottom'])) {
|
Chris@0
|
56 $scripts_bottom_placeholder = $attachments['html_response_attachment_placeholders']['scripts_bottom'];
|
Chris@0
|
57 $content = $response->getContent();
|
Chris@0
|
58 $content = str_replace($scripts_bottom_placeholder, '<drupal-big-pipe-scripts-bottom-marker>' . $scripts_bottom_placeholder . '<drupal-big-pipe-scripts-bottom-marker>', $content);
|
Chris@0
|
59 $response->setContent($content);
|
Chris@0
|
60 }
|
Chris@0
|
61 }
|
Chris@0
|
62
|
Chris@0
|
63 /**
|
Chris@0
|
64 * Transforms a HtmlResponse to a BigPipeResponse.
|
Chris@0
|
65 *
|
Chris@0
|
66 * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
|
Chris@0
|
67 * The event to process.
|
Chris@0
|
68 */
|
Chris@0
|
69 public function onRespond(FilterResponseEvent $event) {
|
Chris@0
|
70 $response = $event->getResponse();
|
Chris@0
|
71 if (!$response instanceof HtmlResponse) {
|
Chris@0
|
72 return;
|
Chris@0
|
73 }
|
Chris@0
|
74
|
Chris@0
|
75 $attachments = $response->getAttachments();
|
Chris@0
|
76
|
Chris@0
|
77 // If there are no no-JS BigPipe placeholders, unwrap the scripts_bottom
|
Chris@0
|
78 // markup.
|
Chris@0
|
79 // @see onRespondEarly()
|
Chris@0
|
80 // @see \Drupal\big_pipe\Render\BigPipe::sendPreBody()
|
Chris@0
|
81 if (empty($attachments['big_pipe_nojs_placeholders'])) {
|
Chris@0
|
82 $content = $response->getContent();
|
Chris@0
|
83 $content = str_replace('<drupal-big-pipe-scripts-bottom-marker>', '', $content);
|
Chris@0
|
84 $response->setContent($content);
|
Chris@0
|
85 }
|
Chris@0
|
86
|
Chris@0
|
87 // If there are neither BigPipe placeholders nor no-JS BigPipe placeholders,
|
Chris@0
|
88 // there isn't anything dynamic in this response, and we can return early:
|
Chris@0
|
89 // there is no point in sending this response using BigPipe.
|
Chris@0
|
90 if (empty($attachments['big_pipe_placeholders']) && empty($attachments['big_pipe_nojs_placeholders'])) {
|
Chris@0
|
91 return;
|
Chris@0
|
92 }
|
Chris@0
|
93
|
Chris@0
|
94 $big_pipe_response = new BigPipeResponse($response);
|
Chris@0
|
95 $big_pipe_response->setBigPipeService($this->getBigPipeService($event));
|
Chris@0
|
96 $event->setResponse($big_pipe_response);
|
Chris@0
|
97 }
|
Chris@0
|
98
|
Chris@0
|
99 /**
|
Chris@0
|
100 * Returns the BigPipe service to use to send the current response.
|
Chris@0
|
101 *
|
Chris@0
|
102 * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
|
Chris@0
|
103 * A response event.
|
Chris@0
|
104 *
|
Chris@0
|
105 * @return \Drupal\big_pipe\Render\BigPipe
|
Chris@0
|
106 * The BigPipe service.
|
Chris@0
|
107 */
|
Chris@0
|
108 protected function getBigPipeService(FilterResponseEvent $event) {
|
Chris@0
|
109 return $this->bigPipe;
|
Chris@0
|
110 }
|
Chris@0
|
111
|
Chris@0
|
112 /**
|
Chris@0
|
113 * {@inheritdoc}
|
Chris@0
|
114 */
|
Chris@0
|
115 public static function getSubscribedEvents() {
|
Chris@0
|
116 // Run after HtmlResponsePlaceholderStrategySubscriber (priority 5), i.e.
|
Chris@0
|
117 // after BigPipeStrategy has been applied, but before normal (priority 0)
|
Chris@0
|
118 // response subscribers have been applied, because by then it'll be too late
|
Chris@0
|
119 // to transform it into a BigPipeResponse.
|
Chris@0
|
120 $events[KernelEvents::RESPONSE][] = ['onRespondEarly', 3];
|
Chris@0
|
121
|
Chris@0
|
122 // Run as the last possible subscriber.
|
Chris@0
|
123 $events[KernelEvents::RESPONSE][] = ['onRespond', -10000];
|
Chris@0
|
124
|
Chris@0
|
125 return $events;
|
Chris@0
|
126 }
|
Chris@0
|
127
|
Chris@0
|
128 }
|