Verified Commit ddfc567d authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3395776 by catch, kristiaanvandeneynde, Wim Leers, Fabianx, larowlan:...

Issue #3395776 by catch, kristiaanvandeneynde, Wim Leers, Fabianx, larowlan: Make POST requests render cacheable

(cherry picked from commit ffa4daa3)
parent 9f4b1f4e
Loading
Loading
Loading
Loading
Loading
+7 −4
Original line number Diff line number Diff line
@@ -348,10 +348,9 @@ public function buildForm($form_arg, FormStateInterface &$form_state) {
    // throwing an exception.
    // @see Drupal\Core\EventSubscriber\EnforcedFormResponseSubscriber
    //
    // @todo Exceptions should not be used for code flow control. However, the
    //   Form API does not integrate with the HTTP Kernel based architecture of
    //   Drupal 8. In order to resolve this issue properly it is necessary to
    //   completely separate form submission from rendering.
    // @todo Exceptions should not be used for code flow control. In order to
    //   resolve this issue properly it is necessary to completely separate form
    //   submission from rendering.
    //   @see https://www.drupal.org/node/2367555
    if ($response instanceof Response) {
      throw new EnforcedResponseException($response);
@@ -831,6 +830,10 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
      }
    }

    // Add the 'CACHE_MISS_IF_UNCACHEABLE_HTTP_METHOD:form' cache tag to
    // identify this render array as a form to the render cache.
    $form['#cache']['tags'][] = 'CACHE_MISS_IF_UNCACHEABLE_HTTP_METHOD:form';

    // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
    // hook_form_FORM_ID_alter() implementations.
    $hooks = ['form'];
+3 −5
Original line number Diff line number Diff line
@@ -96,10 +96,6 @@ public function __construct(RequestStack $request_stack, $cache_factory, CacheCo
   * {@inheritdoc}
   */
  public function get(array $elements) {
    // @todo remove this check when https://www.drupal.org/node/2367555 lands.
    if (!$this->requestStack->getCurrentRequest()->isMethodCacheable()) {
      return FALSE;
    }

    // When rendering placeholders, special case auto-placeholdered elements:
    // avoid retrieving them from cache again, or rendering them again.
@@ -130,7 +126,9 @@ public function get(array $elements) {
  public function set(array &$elements, array $pre_bubbling_elements) {
    $result = parent::set($elements, $pre_bubbling_elements);

    // @todo remove this check when https://www.drupal.org/node/2367555 lands.
    // Writes to the render cache are disabled on uncacheable HTTP requests, to
    // prevent very low hit rate items from being written. If we're not writing
    // to the cache, there's also no benefit to placeholdering either.
    if (!$this->requestStack->getCurrentRequest()->isMethodCacheable()) {
      return FALSE;
    }
+9 −9
Original line number Diff line number Diff line
@@ -59,16 +59,17 @@ public function __construct(RequestStack $request_stack, $cache_factory, CacheCo
   * {@inheritdoc}
   */
  public function get(array $elements) {
    // Form submissions rely on the form being built during the POST request,
    // and render caching of forms prevents this from happening.
    // @todo remove the isMethodCacheable() check when
    //   https://www.drupal.org/node/2367555 lands.
    if (!$this->requestStack->getCurrentRequest()->isMethodCacheable() || !$this->isElementCacheable($elements)) {
    if (!$this->isElementCacheable($elements)) {
      return FALSE;
    }

    $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'render';
    if (($cache_bin = $this->cacheFactory->get($bin)) && $cache = $cache_bin->get($elements['#cache']['keys'], CacheableMetadata::createFromRenderArray($elements))) {
      if (!$this->requestStack->getCurrentRequest()->isMethodCacheable()) {
        if (!empty(array_filter($cache->tags, fn (string $tag) => str_starts_with($tag, 'CACHE_MISS_IF_UNCACHEABLE_HTTP_METHOD:')))) {
          return FALSE;
        }
      }
      return $cache->data;
    }
    return FALSE;
@@ -78,10 +79,9 @@ public function get(array $elements) {
   * {@inheritdoc}
   */
  public function set(array &$elements, array $pre_bubbling_elements) {
    // Form submissions rely on the form being built during the POST request,
    // and render caching of forms prevents this from happening.
    // @todo remove the isMethodCacheable() check when
    //   https://www.drupal.org/node/2367555 lands.
    // Avoid setting cache items on POST requests, this ensures that cache items
    // with a very low hit rate won't enter the cache. All render elements
    // except forms will still be retrieved from cache when available.
    if (!$this->requestStack->getCurrentRequest()->isMethodCacheable() || !$this->isElementCacheable($elements)) {
      return FALSE;
    }
+5 −3
Original line number Diff line number Diff line
@@ -394,9 +394,11 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
    }
    // If instructed to create a placeholder, and a #lazy_builder callback is
    // present (without such a callback, it would be impossible to replace the
    // placeholder), replace the current element with a placeholder.
    // @todo remove the isMethodCacheable() check when
    //   https://www.drupal.org/node/2367555 lands.
    // placeholder), replace the current element with a placeholder. On
    // uncacheable requests, always skip placeholdering - if a form is inside
    // a placeholder, which is likely, we want to render it as soon as possible,
    // so that form submission and redirection can take over before any more
    // content is rendered.
    if (isset($elements['#create_placeholder']) && $elements['#create_placeholder'] === TRUE && $this->requestStack->getCurrentRequest()->isMethodCacheable()) {
      if (!isset($elements['#lazy_builder'])) {
        throw new \LogicException('When #create_placeholder is set, a #lazy_builder callback must be present as well.');
+5 −1
Original line number Diff line number Diff line
@@ -108,7 +108,11 @@ public function __construct(SessionConfigurationInterface $session_configuration
  public function processPlaceholders(array $placeholders) {
    $request = $this->requestStack->getCurrentRequest();

    // @todo remove this check when https://www.drupal.org/node/2367555 lands.
    // Prevent placeholders from being processed by BigPipe on uncacheable
    // request methods. For example, a form rendered inside a placeholder will
    // be rendered as soon as possible before any headers are sent, so that it
    // can be detected, submitted, and redirected immediately.
    // @todo https://www.drupal.org/node/2367555
    if (!$request->isMethodCacheable()) {
      return [];
    }
Loading