sessionless-1.x-dev/modules/sessionless_forms/src/SessionlessFormCache.php

modules/sessionless_forms/src/SessionlessFormCache.php
<?php

namespace Drupal\sessionless_forms;

use Drupal\Component\Utility\Crypt;
use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormCacheInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\Session\AccountInterface;

/**
 * Sessionless form cache.
 *
 * Copy of FormCache, but storing in encrypted form fields.
 *
 * @see \Drupal\Core\Form\FormCache
 * @see \Drupal\Core\Form\FormBuilder
 * @see \sessionless_forms_form_alter
 */
class SessionlessFormCache implements FormCacheInterface {

  public function __construct(
    protected string $root,
    protected ModuleHandlerInterface $moduleHandler,
    protected AccountInterface $currentUser,
    protected CsrfTokenGenerator $csrfToken,
    protected LoggerChannelInterface $logger,
    protected SessionlessFormHiddenFieldHandler $sessionlessFormLazyBuilder,
  ) {}

  public function getCache($form_build_id, FormStateInterface $form_state) {
    $form = NULL;
    $userInput = $form_state->getUserInput();
    if ($storedForm = $this->sessionlessFormLazyBuilder->getForm($userInput)) {
      $form = $storedForm;
      if ($stored_form_state = $this->sessionlessFormLazyBuilder->getFormStateArray($userInput)) {
        // Re-populate $form_state for subsequent rebuilds.
        $form_state->setFormState($stored_form_state);

        // If the original form is contained in include files, load the files.
        // @see \Drupal\Core\Form\FormStateInterface::loadInclude()
        $build_info = $form_state->getBuildInfo();
        $build_info += ['files' => []];
        foreach ($build_info['files'] as $file) {
          if (is_array($file)) {
            $file += ['type' => 'inc', 'name' => $file['module']];
            $this->moduleHandler->loadInclude($file['module'], $file['type'], $file['name']);
          }
          elseif (file_exists($file)) {
            require_once $this->root . '/' . $file;
          }
        }
      }
      // Generate a new #build_id if the cached form was rendered on a
      // cacheable page.
      $build_info = $form_state->getBuildInfo();
      if (!empty($build_info['immutable'])) {
        $form['#build_id_old'] = $form['#build_id'];
        $form['#build_id'] = 'form-' . Crypt::randomBytesBase64();
        $form['form_build_id']['#value'] = $form['#build_id'];
        $form['form_build_id']['#id'] = $form['#build_id'];
        unset($build_info['immutable']);
        $form_state->setBuildInfo($build_info);
      }
    }
    return $form;
  }

  /**
   * Set cache.
   *
   * Called in FormBuilder::setCache() (L458), which is called in:
   * - FormBuilder::rebuildForm(L436), which runs after ::prepareForm, which
   *   invokes form_alter hooks
   * - FormBuilder::processForm(L640) storing $unprocessedForm
   *   - which runs after ::prepareForm when called in ::buildForm()(L320)
   *   - which runs after ::prepareForm when called in ::submitForm()(L498)
   * So we can always assume that hook_form_alter ran before this, and rely on
   * the hack used there.
   */
  public function setCache($form_build_id, $form, FormStateInterface $form_state) {
    if (isset($form['#build_id']) && $form['#build_id'] != $form_build_id) {
      $this->logger->error('Form build-id mismatch detected while attempting to store a form in the cache.');
      return;
    }

    // Cache form structure.
    if (isset($form)) {
      if ($this->currentUser->isAuthenticated()) {
        $form['#cache_token'] = $this->csrfToken->get();
      }
      unset($form['#build_id_old']);
      $this->sessionlessFormLazyBuilder->setForm($form_state, $form);
    }

    if ($form_state->getCacheableArray()) {
      $this->sessionlessFormLazyBuilder->setFormState($form_state);
    }
  }

  public function deleteCache($form_build_id) {}

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc