sessionless-1.x-dev/modules/sessionless_forms/src/SessionlessFormHiddenFieldHandler.php
modules/sessionless_forms/src/SessionlessFormHiddenFieldHandler.php
<?php
declare(strict_types=1);
namespace Drupal\sessionless_forms;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Security\TrustedCallbackInterface;
use Drupal\sessionless\SessionlessInterface;
final class SessionlessFormHiddenFieldHandler implements TrustedCallbackInterface {
public function __construct(
protected SessionlessInterface $webTokenService
) {}
public function preRender(array $element): array {
if ($serializedForm = $element['#sessionless_forms__serialized_form']) {
$element['form'] = [
'#type' => 'hidden',
'#name' => 'sessionless_forms__form',
'#value' => $this->webTokenService->encode($serializedForm),
];
}
if ($serializedFormState = $element['#sessionless_forms__serialized_form_state']) {
$element['form_state'] = [
'#type' => 'hidden',
'#name' => 'sessionless_forms__form_state',
'#value' => $this->webTokenService->encode($serializedFormState),
];
}
return $element;
}
public static function hookFormAlter(array &$form, FormStateInterface $form_state, $form_id) {
// Add the lazy builder for our hidden fields.
$form['sessionless_forms'] = [
'#cache' => ['max-age' => 0],
'#pre_render' => [
'sessionless_forms.hidden_field_handler:preRender',
],
'#sessionless_forms__serialized_form' => NULL,
'#sessionless_forms__serialized_form_state' => NULL,
];
// Store a ref to an alterable form.
// Sorry kittens, this seems the only way to get that currently.
$storage =& $form_state->getStorage();
$storage['sessionless__alterable_form'] =& $form;
$form_state->setStorage($storage);
}
public function resetValues(array &$form) {
$form['sessionless_forms']['#sessionless_forms__serialized_form'] = NULL;
$form['sessionless_forms']['#sessionless_forms__serialized_form_state'] = NULL;
}
public function setForm(FormStateInterface $formState, $form) {
$alterableForm =& $formState->get('sessionless__alterable_form');
if (!is_array($alterableForm)) {
throw new \UnexpectedValueException('CompleteForm must be set here.');
}
$copiedForm = $form;
$this->resetValues($copiedForm);
$serialized = serialize($copiedForm);
$alterableForm['sessionless_forms']['#sessionless_forms__serialized_form'] = $serialized;
}
public function setFormState(FormStateInterface $formState) {
$alterableForm =& $formState->get('sessionless__alterable_form');
if (!is_array($alterableForm)) {
throw new \UnexpectedValueException('CompleteForm must be set here.');
}
$cacheableArray = $formState->getCacheableArray();
unset($cacheableArray['storage']['sessionless__alterable_form']);
$serialized = serialize($cacheableArray);
$alterableForm['sessionless_forms']['#sessionless_forms__serialized_form_state'] = $serialized;
}
public function getForm(array $userInput): ?array {
$webToken = $userInput['sessionless_forms__form'] ?? NULL;
if ($webToken) {
$serialized = $this->webTokenService->decode($webToken);
// Unserializing is safe here, as the value is generated in a trusted way,
// then signed and encrypted.
return unserialize($serialized);
}
return NULL;
}
public function getFormStateArray(array $userInput): ?array {
$webToken = $userInput['sessionless_forms__form_state'] ?? NULL;
if ($webToken) {
$serialized = $this->webTokenService->decode($webToken);
// Unserializing is safe here, as the value is generated in a trusted way,
// then signed and encrypted.
return unserialize($serialized);
}
return NULL;
}
public static function trustedCallbacks() {
return ['preRender'];
}
}
