From 70326e6d7b2e5261cc01237ba25e91d0afb8b050 Mon Sep 17 00:00:00 2001 From: Daniel Lienert Date: Tue, 3 May 2022 16:48:39 +0200 Subject: [PATCH 1/3] FEATURE: Make formState initialization extendable This adds a mechanism to hook into the form state initialization and define custom initializers. Resolves: #152 --- Classes/Core/Model/FormDefinition.php | 26 ++++++++++-- Classes/Core/Runtime/FormRuntime.php | 18 +-------- .../FormState/DefaultFormStateInitializer.php | 40 +++++++++++++++++++ .../FormStateInitializerInterface.php | 23 +++++++++++ Configuration/Settings.yaml | 1 + composer.json | 2 +- 6 files changed, 89 insertions(+), 21 deletions(-) create mode 100644 Classes/FormState/DefaultFormStateInitializer.php create mode 100644 Classes/FormState/FormStateInitializerInterface.php diff --git a/Classes/Core/Model/FormDefinition.php b/Classes/Core/Model/FormDefinition.php index 9564e09..0575b6d 100644 --- a/Classes/Core/Model/FormDefinition.php +++ b/Classes/Core/Model/FormDefinition.php @@ -18,6 +18,8 @@ use Neos\Form\Exception; use Neos\Form\Exception\IdentifierNotValidException; use Neos\Form\Exception\TypeDefinitionNotFoundException; +use Neos\Form\FormState\DefaultFormStateInitializer; +use Neos\Form\FormState\FormStateInitializerInterface; use Neos\Form\Utility\Arrays as FormArrays; use Neos\Form\Utility\SupertypeResolver; use Neos\Utility\Arrays; @@ -318,6 +320,8 @@ class FormDefinition extends Renderable\AbstractCompositeRenderable */ protected $finisherPresets; + protected FormStateInitializerInterface $formStateInitializer; + /** * Constructor. Creates a new FormDefinition with the given identifier. * @@ -329,13 +333,20 @@ class FormDefinition extends Renderable\AbstractCompositeRenderable */ public function __construct($identifier, $formDefaults = [], $type = 'Neos.Form:Form') { - $this->formFieldTypeManager = new SupertypeResolver(isset($formDefaults['formElementTypes']) ? $formDefaults['formElementTypes'] : []); - $this->validatorPresets = isset($formDefaults['validatorPresets']) ? $formDefaults['validatorPresets'] : []; - $this->finisherPresets = isset($formDefaults['finisherPresets']) ? $formDefaults['finisherPresets'] : []; + $this->formFieldTypeManager = new SupertypeResolver($formDefaults['formElementTypes'] ?? []); + $this->validatorPresets = $formDefaults['validatorPresets'] ?? []; + $this->finisherPresets = $formDefaults['finisherPresets'] ?? []; if (!is_string($identifier) || strlen($identifier) === 0) { throw new IdentifierNotValidException('The given identifier was not a string or the string was empty.', 1325574803); } + + $formStateInitializer = $formDefaults['formStateInitializer'] ?? DefaultFormStateInitializer::class; + if (!is_a($formStateInitializer, FormStateInitializerInterface::class, true)) { + throw new \RuntimeException(sprintf('The given class "%s" does not implement the interface %s', $formStateInitializer, FormStateInitializerInterface::class), 1648204540); + } + $this->formStateInitializer = new $formStateInitializer(); + $this->identifier = $identifier; $this->type = $type; @@ -703,4 +714,13 @@ public function getValidatorPresets() { return $this->validatorPresets; } + + /** + * @internal + * @return FormStateInitializerInterface + */ + public function getFormStateInitializer(): FormStateInitializerInterface + { + return $this->formStateInitializer; + } } diff --git a/Classes/Core/Runtime/FormRuntime.php b/Classes/Core/Runtime/FormRuntime.php index 543e706..c0ba106 100644 --- a/Classes/Core/Runtime/FormRuntime.php +++ b/Classes/Core/Runtime/FormRuntime.php @@ -162,7 +162,7 @@ public function __construct(FormDefinition $formDefinition, ActionRequest $reque */ public function initializeObject() { - $this->initializeFormStateFromRequest(); + $this->formState = $this->formDefinition->getFormStateInitializer()->initializeFormState($this->formDefinition, $this->request); $this->initializeCurrentPageFromRequest(); if (!$this->isFirstRequest()) { @@ -170,22 +170,6 @@ public function initializeObject() } } - /** - * @return void - * @internal - */ - protected function initializeFormStateFromRequest() - { - $serializedFormStateWithHmac = $this->request->getInternalArgument('__state'); - if ($serializedFormStateWithHmac === null) { - $this->formState = new FormState(); - } else { - $serializedFormState = $this->hashService->validateAndStripHmac($serializedFormStateWithHmac); - /** @noinspection UnserializeExploitsInspection The unserialize call is safe because of the HMAC check above */ - $this->formState = unserialize(base64_decode($serializedFormState)); - } - } - /** * @return void * @internal diff --git a/Classes/FormState/DefaultFormStateInitializer.php b/Classes/FormState/DefaultFormStateInitializer.php new file mode 100644 index 0000000..13d1921 --- /dev/null +++ b/Classes/FormState/DefaultFormStateInitializer.php @@ -0,0 +1,40 @@ +getInternalArgument('__state'); + if ($serializedFormStateWithHmac !== null) { + $serializedFormState = $this->hashService->validateAndStripHmac($serializedFormStateWithHmac); + /** @noinspection UnserializeExploitsInspection The unserialize call is safe because of the HMAC check above */ + return unserialize(base64_decode($serializedFormState)); + } + + return new FormState(); + } +} diff --git a/Classes/FormState/FormStateInitializerInterface.php b/Classes/FormState/FormStateInitializerInterface.php new file mode 100644 index 0000000..8a108a1 --- /dev/null +++ b/Classes/FormState/FormStateInitializerInterface.php @@ -0,0 +1,23 @@ +=7.1.0", + "php": ">=7.4.0", "neos/flow": ">=6.0 || dev-master" }, "replace": { From 8e04d3be2a300ef45bc276f85d46b1e887ce94ef Mon Sep 17 00:00:00 2001 From: Daniel Lienert Date: Tue, 3 May 2022 16:54:20 +0200 Subject: [PATCH 2/3] TASK: Test with PHP 7.3 and PHP 8.1 --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1af07f9..d95b528 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,8 +19,8 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['7.3', '7.4'] - flow-versions: ['7.0'] + php-versions: ['7.4', '8.1'] + flow-versions: ['7.3'] dependencies: ['highest'] defaults: @@ -90,7 +90,7 @@ jobs: EOF - name: Run Unit tests - run: | + run: | bin/phpunit --colors -c Build/BuildEssentials/PhpUnit/UnitTests.xml Packages/Application/${PACKAGE_NAME}/Tests/Unit - name: Run Functional tests From a6591963367ec19e7df89a292c31116d630ab5f9 Mon Sep 17 00:00:00 2001 From: Daniel Lienert Date: Tue, 3 May 2022 16:59:50 +0200 Subject: [PATCH 3/3] TASK: Minor style and code fixes --- Classes/Core/Model/FormDefinition.php | 9 ++++----- Classes/FormState/DefaultFormStateInitializer.php | 3 ++- Classes/FormState/FormStateInitializerInterface.php | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Classes/Core/Model/FormDefinition.php b/Classes/Core/Model/FormDefinition.php index 0575b6d..f4fcab4 100644 --- a/Classes/Core/Model/FormDefinition.php +++ b/Classes/Core/Model/FormDefinition.php @@ -341,11 +341,11 @@ public function __construct($identifier, $formDefaults = [], $type = 'Neos.Form: throw new IdentifierNotValidException('The given identifier was not a string or the string was empty.', 1325574803); } - $formStateInitializer = $formDefaults['formStateInitializer'] ?? DefaultFormStateInitializer::class; - if (!is_a($formStateInitializer, FormStateInitializerInterface::class, true)) { - throw new \RuntimeException(sprintf('The given class "%s" does not implement the interface %s', $formStateInitializer, FormStateInitializerInterface::class), 1648204540); + $formStateInitializerClass = $formDefaults['formStateInitializer'] ?? DefaultFormStateInitializer::class; + if (!is_a($formStateInitializerClass, FormStateInitializerInterface::class, true)) { + throw new \RuntimeException(sprintf('The given class "%s" does not implement the interface %s', $formStateInitializerClass, FormStateInitializerInterface::class), 1648204540); } - $this->formStateInitializer = new $formStateInitializer(); + $this->formStateInitializer = new $formStateInitializerClass(); $this->identifier = $identifier; $this->type = $type; @@ -717,7 +717,6 @@ public function getValidatorPresets() /** * @internal - * @return FormStateInitializerInterface */ public function getFormStateInitializer(): FormStateInitializerInterface { diff --git a/Classes/FormState/DefaultFormStateInitializer.php b/Classes/FormState/DefaultFormStateInitializer.php index 13d1921..20cda49 100644 --- a/Classes/FormState/DefaultFormStateInitializer.php +++ b/Classes/FormState/DefaultFormStateInitializer.php @@ -13,10 +13,11 @@ * source code. */ +use Neos\Flow\Annotations as Flow; use Neos\Flow\Mvc\ActionRequest; use Neos\Flow\Security\Cryptography\HashService; use Neos\Form\Core\Model\FormDefinition; -use Neos\Fusion\Form\Runtime\Domain\FormState; +use Neos\Form\Core\Runtime\FormState; class DefaultFormStateInitializer implements FormStateInitializerInterface { diff --git a/Classes/FormState/FormStateInitializerInterface.php b/Classes/FormState/FormStateInitializerInterface.php index 8a108a1..d6160bf 100644 --- a/Classes/FormState/FormStateInitializerInterface.php +++ b/Classes/FormState/FormStateInitializerInterface.php @@ -15,7 +15,7 @@ use Neos\Flow\Mvc\ActionRequest; use Neos\Form\Core\Model\FormDefinition; -use Neos\Fusion\Form\Runtime\Domain\FormState; +use Neos\Form\Core\Runtime\FormState; interface FormStateInitializerInterface {