Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: Make formState initialization extendable #154

Merged
merged 3 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
25 changes: 22 additions & 3 deletions Classes/Core/Model/FormDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -318,6 +320,8 @@ class FormDefinition extends Renderable\AbstractCompositeRenderable
*/
protected $finisherPresets;

protected FormStateInitializerInterface $formStateInitializer;

/**
* Constructor. Creates a new FormDefinition with the given identifier.
*
Expand All @@ -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);
}

$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 $formStateInitializerClass();

$this->identifier = $identifier;
$this->type = $type;

Expand Down Expand Up @@ -703,4 +714,12 @@ public function getValidatorPresets()
{
return $this->validatorPresets;
}

/**
* @internal
*/
public function getFormStateInitializer(): FormStateInitializerInterface
{
return $this->formStateInitializer;
}
}
18 changes: 1 addition & 17 deletions Classes/Core/Runtime/FormRuntime.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,30 +162,14 @@ 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()) {
$this->processSubmittedFormValues();
}
}

/**
* @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
Expand Down
41 changes: 41 additions & 0 deletions Classes/FormState/DefaultFormStateInitializer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);

namespace Neos\Form\FormState;

/*
* This file is part of the Neos.Form package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* 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\Form\Core\Runtime\FormState;

class DefaultFormStateInitializer implements FormStateInitializerInterface
{
/**
* @Flow\Inject
* @var HashService
*/
protected $hashService;

public function initializeFormState(FormDefinition $formDefinition, ActionRequest $actionRequest): FormState
{
$serializedFormStateWithHmac = $actionRequest->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();
}
}
23 changes: 23 additions & 0 deletions Classes/FormState/FormStateInitializerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);

namespace Neos\Form\FormState;

/*
* This file is part of the Neos.Form package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Neos\Flow\Mvc\ActionRequest;
use Neos\Form\Core\Model\FormDefinition;
use Neos\Form\Core\Runtime\FormState;

interface FormStateInitializerInterface
{
public function initializeFormState(FormDefinition $formDefinition, ActionRequest $actionRequest): FormState;
}
1 change: 1 addition & 0 deletions Configuration/Settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Neos:
title: Default
stylesheets: { }
javaScripts: { }
formStateInitializer: Neos\Form\FormState\DefaultFormStateInitializer
formElementTypes:
'Neos.Form:Base':
renderingOptions:
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"license": "MIT",
"description": "Extensible and flexible API for building web forms",
"require": {
"php": ">=7.1.0",
"php": ">=7.4.0",
"neos/flow": ">=6.0 || dev-master"
},
"replace": {
Expand Down