From 13f56aa19d6d6f238d5facd2a33687171eaee38d Mon Sep 17 00:00:00 2001 From: Darren Oh <2293701+darrenoh@users.noreply.github.com> Date: Thu, 9 Nov 2023 21:34:44 -0500 Subject: [PATCH] Fix indirect array access (#68) * Fix indirect array access * Attempt at decorating form validator * add FormValidator to docs stub * Fix and simplify FormState array access * Move decoration to form builder service * Moved form state decoration to form builders * Simplified form state replacement --------- Co-authored-by: Matt Glaman --- docs/supported-apis.md | 2 + src/Controller/DrupalGetFormController.php | 1 - src/Form/ArrayAccessFormState.php | 46 +++++++++++++++++----- src/Form/FormBuilder.php | 29 ++++++++++++++ src/Provider.php | 7 ++++ src/functions/form.php | 5 --- 6 files changed, 74 insertions(+), 16 deletions(-) create mode 100644 src/Form/FormBuilder.php diff --git a/docs/supported-apis.md b/docs/supported-apis.md index a908684..72765db 100644 --- a/docs/supported-apis.md +++ b/docs/supported-apis.md @@ -42,7 +42,9 @@ See [supported hooks](supported-hooks.md#field-hooks). ## Form API TODO +* Document decorated form state to allow array access * `\Retrofit\Drupal\Controller\DrupalGetFormController` * `\Retrofit\Drupal\Form\DrupalGetForm` * `\Retrofit\Drupal\Form\ArrayAccessFormState` +* `\Retrofit\Drupal\Form\FormBuilder` diff --git a/src/Controller/DrupalGetFormController.php b/src/Controller/DrupalGetFormController.php index 2e31a90..d47ef4f 100644 --- a/src/Controller/DrupalGetFormController.php +++ b/src/Controller/DrupalGetFormController.php @@ -8,7 +8,6 @@ use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormBuilderInterface; -use Drupal\Core\Form\FormState; use Drupal\Core\Routing\RouteMatchInterface; use Retrofit\Drupal\Form\ArrayAccessFormState; use Retrofit\Drupal\Form\DrupalGetForm; diff --git a/src/Form/ArrayAccessFormState.php b/src/Form/ArrayAccessFormState.php index 263c96c..56c5dd4 100644 --- a/src/Form/ArrayAccessFormState.php +++ b/src/Form/ArrayAccessFormState.php @@ -5,28 +5,54 @@ namespace Retrofit\Drupal\Form; use Drupal\Core\Form\FormState; +use Drupal\Core\Url; final class ArrayAccessFormState extends FormState implements \ArrayAccess { public function offsetExists(mixed $offset): bool { - return match ($offset) { - 'values' => true, - default => isset($this->$offset), - }; + return isset($this->$offset); } - public function offsetGet(mixed $offset): mixed + public function &offsetGet(mixed $offset): mixed { - return match ($offset) { - 'values' => $this->getValues(), - default => $this->$offset - }; + switch ($offset) { + case 'complete_form': + return $this->getCompleteForm(); + case 'groups': + return $this->getGroups(); + case 'input': + return $this->getUserInput(); + case 'storage': + return $this->getStorage(); + case 'clicked_button': + case 'triggering_element': + return $this->getTriggeringElement(); + case 'values': + return $this->getValues(); + default: + return $this->$offset; + } } public function offsetSet(mixed $offset, mixed $value): void { - $this->$offset = $value; + switch ($offset) { + case 'redirect': + if (is_string($value) && $url = \Drupal::pathValidator()->getUrlIfValidWithoutAccessCheck($value)) { + $this->redirect = $url; + } elseif (is_array($value)) { + $path = array_shift($value); + if (is_string($path) && $url = \Drupal::pathValidator()->getUrlIfValidWithoutAccessCheck($path)) { + $options = array_shift($value) ?: []; + $url->mergeOptions((array) $options); + $this->redirect = $url; + } + } + break; + default: + $this->$offset = $value; + } } public function offsetUnset(mixed $offset): void diff --git a/src/Form/FormBuilder.php b/src/Form/FormBuilder.php new file mode 100644 index 0000000..b414192 --- /dev/null +++ b/src/Form/FormBuilder.php @@ -0,0 +1,29 @@ +getCacheableArray() + get_object_vars($original_form_state); + } + foreach ($original_form_state as $offset => $value) { + $form_state[$offset] = $value; + } + return parent::buildForm($form_arg, $form_state); + } +} diff --git a/src/Provider.php b/src/Provider.php index d8b073b..f83a8ad 100644 --- a/src/Provider.php +++ b/src/Provider.php @@ -9,6 +9,7 @@ use Drupal\Core\Template\Loader\FilesystemLoader; use Retrofit\Drupal\Controller\RetrofitTitleResolver; use Retrofit\Drupal\Field\FieldTypePluginManager; +use Retrofit\Drupal\Form\FormBuilder; use Retrofit\Drupal\Language\GlobalLanguageContentSetter; use Retrofit\Drupal\Menu\LocalActionManager; use Retrofit\Drupal\Menu\LocalTaskManager; @@ -122,6 +123,12 @@ public function register(ContainerBuilder $container) $container->register(RetrofitTitleResolver::class) ->setDecoratedService('title_resolver') ->addArgument(new Reference(RetrofitTitleResolver::class . '.inner')); + + $container->setDefinition( + FormBuilder::class, + (new ChildDefinition('form_builder')) + ->setDecoratedService('form_builder') + ); } public function alter(ContainerBuilder $container) diff --git a/src/functions/form.php b/src/functions/form.php index 9fd46ba..7fb894b 100644 --- a/src/functions/form.php +++ b/src/functions/form.php @@ -14,11 +14,6 @@ function drupal_build_form(string $form_id, array &$form_state): array { $form_object = \Drupal::classResolver(DrupalGetForm::class); $form_object->setFormId($form_id); - $original_form_state = $form_state; - $form_state = new ArrayAccessFormState(); - foreach ($original_form_state as $offset => $value) { - $form_state[$offset] = $value; - } return \Drupal::formBuilder()->buildForm($form_object, $form_state); }