Skip to content

Commit

Permalink
Fix indirect array access (#68)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
darrenoh and mglaman authored Nov 10, 2023
1 parent 2930956 commit 13f56aa
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 16 deletions.
2 changes: 2 additions & 0 deletions docs/supported-apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
1 change: 0 additions & 1 deletion src/Controller/DrupalGetFormController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
46 changes: 36 additions & 10 deletions src/Form/ArrayAccessFormState.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 29 additions & 0 deletions src/Form/FormBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Retrofit\Drupal\Form;

use Drupal\Core\Form\FormBuilder as CoreFormBuilder;
use Drupal\Core\Form\FormStateInterface;
use Retrofit\Drupal\Form\ArrayAccessFormState;

class FormBuilder extends CoreFormBuilder
{
/**
* @param FormStateInterface|mixed[] $form_state
* @return mixed[]
*/
public function buildForm(mixed $form_arg, FormStateInterface|array &$form_state): array
{
$original_form_state = $form_state;
$form_state = new ArrayAccessFormState();
if ($original_form_state instanceof FormStateInterface) {
$original_form_state = $original_form_state->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);
}
}
7 changes: 7 additions & 0 deletions src/Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 0 additions & 5 deletions src/functions/form.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down

0 comments on commit 13f56aa

Please sign in to comment.