[Form] Add an allow_array_submission option to let PRE_SUBMIT listeners transform submitted arrays - #65515
#65515[Form] Add an allow_array_submission option to let PRE_SUBMIT listeners transform submitted arrays#65515nicolas-grekas wants to merge 1 commit intosymfony:8.2symfony/symfony:8.2from nicolas-grekas:sweep/47093-form-presubmit-array-datanicolas-grekas/symfony:sweep/47093-form-presubmit-array-dataCopy head branch name to clipboard
Conversation
04917f7 to
cb952fd
Compare
cb952fd to
44c031d
Compare
|
I reworked this as an opt-in feature on 8.2, through a new Moving the check after The option mirrors |
d587ff2 to
e1922b5
Compare
…ers turn a submitted array into data the form accepts
e1922b5 to
51d7ec4
Compare
Form::submit()rejects array data submitted to a form that is neither compound normultiple. It does so beforePRE_SUBMITis dispatched: the data is replaced bynull, aTransformationFailedExceptionis recorded, and listeners are never called. Applications that submit JSON payloads cannot map such a payload to a scalar field, because no listener ever sees the value.This PR adds an
allow_array_submissionoption toFormType, defaulting tofalse.When the option is enabled, the check moves after
PRE_SUBMIT: listeners receive the submitted array and can replace it with data the form accepts. The deferred check then applies the same rules as the eager one. Data that is still an array fails with the same exception message as today, unless a listener turned it into a file upload: such data is accepted whenallow_file_uploadis true, and reported as "file upload given" otherwise, which is what the same value gets when it is submitted directly. The option mirrorsallow_file_upload, which gates the sibling branch of the same check.When the option is left to
false, nothing changes: listeners are not called for array data, and the form fails exactly as before, with the same message and the samenullview data.Why opt-in
An earlier revision of this PR moved the check unconditionally and targeted 6.4 as a bugfix. That changes the input domain of every deployed
PRE_SUBMITlistener on a scalar field: any client can postfield[]=x, so listeners written against the "never an array" guarantee become reachable with client-controlled arrays. Three core types needed guards for exactly that reason, and third-party listeners would have been exposed the same way with no guard at all. The option keeps the old guarantee unless a form opts in, and scopes the new listener contract to that form.Core listeners
With the option enabled, the listeners registered by core types can receive arrays too, so the three affected ones now handle them:
FileTypewithmultiple: falsereplaces anything that is not a file upload bynull, so an array would validate as "no file uploaded" with no error at all. It now leaves arrays alone, so the form reports them.ColorTypewithhtml5: trueadded its own "This value is not a valid HTML5 color." error for arrays, even when a later listener turned the value into a valid color. It now skips arrays, so the html5 check applies to values a listener has normalized.TimeTypewithwidget: single_textpassed the submitted value straight topreg_match(), which raises aTypeErroron arrays. Both of its listeners now check for a string first, and the form reports "Please enter a valid time." instead.The
FileTypeandColorTypeguards are gated on the option, so a form that does not opt in keeps the behavior it has today, including for an array set by aPRE_SUBMITlistener registered at a priority above0. TheTimeTypeguard is not gated: an array can reach those listeners today through such a priority, and aTypeErroris never the right report.On a
ChoiceTypefield that is neithermultiplenorexpandedand that enables the option, the type's ownPRE_SUBMITlistener runs first: a nested invalid array is reported as "All choices submitted must be NULL, strings or ints.", and an array that survives the listener is rejected by the deferred check. Nothing changes for choice fields that do not enable the option.Checks
Run on this branch with PHP 8.5 and PHPUnit 13.3:
./phpunit src/Symfony/Component/Form: 5100 tests, 10773 assertions, 384 skipped, 8 incomplete, no failure. The base commit runs 5085 tests with the same skipped and incomplete counts.FormType.Revert verified, running the same suite in three states:
The option "allow_array_submission" does not exist, the four descriptor fixtures no longer match, and theSimpleFormTestcases built on a rawFormBuilderfail on their behavior assertions.testArrayDataIsRejectedBeforeListenersByDefault,ColorTypeTest::testArraySetByPreSubmitListenersIsReportedWhenNotAllowedandFileTypeTest::testArraySetByPreSubmitListenersIsDiscardedWhenNotAllowedstill pass, since they protect existing behavior.TimeTypeTestraisesTypeError: preg_match(): Argument #2 ($subject) must be of type string, array given,ColorTypeTest::testSubmitArrayWhenAllowedgets "This value is not a valid HTML5 color." instead of "Please select a valid color.", and theFileTypeTestcases report the form as synchronized or lose the resolved upload.For the documentation
allow_array_submissionin the form options reference: what it enables, defaultfalse.PRE_SUBMITlistener that maps the submitted array to the field's scalar value.PRE_SUBMITlistener on that form must be ready to receive an array; core type listeners already are.allow_file_uploadis true.