-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Form] Fix ChoiceType to ensure submitted data is not nested unnecessarily #21267
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
Conversation
81092ad
to
9d406db
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @issei-m, sorry I took some time to answer here, although your concern is legit this should never happen in an html context but using forms with json API might break indeed.
Since 2.x is still, maintained for years, maybe we should merge your patch, but please note that I'm working on solving this issue more globally in master and will propose a PR soon, to avoid patching in form types forever and stumbling upon weird side effects.
I'm a bit afraid that if we accept this PR, we'll end up with PRE_SUBMIT validation listeners everywhere, as that would be bad for performances too.
I've left some minor comments but then I would be +0.
What do you think symfony deciders?
@@ -65,6 +65,22 @@ public function __construct(ChoiceListFactoryInterface $choiceListFactory = null | |||
*/ | |||
public function buildForm(FormBuilderInterface $builder, array $options) | |||
{ | |||
// To avoid some problem against treating of array (e.g. Array to string conversion), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please move this new block at the end of the method?
} | ||
|
||
foreach ($data as $v) { | ||
if (is_array($v)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be:
if (null !== $v && !is_string($v)) {
throw new TransformationFailedException('All choices submitted must be NULL or strings.');
}
@HeahDude Just I've fixed point of mentioned by you. |
} | ||
|
||
foreach ($data as $v) { | ||
if (null !== $v && !is_string($v)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of adding a new event listener we could do this in the already existing one before flipping the data, can't we?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The listener you mentioned is only added when choice is multiple. Even though we would do so we still need to add a new listener for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I missed that.
@@ -160,6 +160,22 @@ public function buildForm(FormBuilderInterface $builder, array $options) | |||
// transformation is merged back into the original collection | |||
$builder->addEventSubscriber(new MergeCollectionListener(true, true)); | |||
} | |||
|
|||
// To avoid some problem against treating of array (e.g. Array to string conversion), | |||
// we have to first ensure the all elements of submitted data ain't an array. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"To avoid issues when the submitted choices are arrays (i.e. array to string conversions), we have to ensure that all elements of the submitted choice data are strings or null."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed up
One last thing that I missed during my previous review: Please move the data provider after the test method. |
@xabbuh It's done. Thanks for your advice! |
👍 Status: Reviewed |
Thank you @issei-m. |
…ed unnecessarily (issei-m) This PR was squashed before being merged into the 2.7 branch (closes #21267). Discussion ---------- [Form] Fix ChoiceType to ensure submitted data is not nested unnecessarily | Q | A | ------------- | --- | Branch? | 2.7 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - Fixed ChoiceType to protect against some problem caused by treating of array. Let's say we have the choice-form like: ```php $form = $factory->create(ChoiceType, null, [ 'choices' => [ 'A', 'B', 'C', ], 'expanded' => true, 'multiple' => true, ]); ``` Then, submit data like this: ```php $form->submit([ [], // unnecessality nested ]); ``` (Yes, I agree in most cases these situation doesn't happen, but can be) Then, we get `array_flip(): Can only flip STRING and INTEGER values!` error at [here](https://github.com/symfony/symfony/blob/6babdb3296a5fbbd9c69d1e3410adbdf749959ef/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php#L114). Even if form is not `multiple`, annoying `Array to string conversion` error occurs in [here](https://github.com/symfony/symfony/blob/6babdb3296a5fbbd9c69d1e3410adbdf749959ef/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php#L144) (via [ChoicesToValuesTransformer](https://github.com/symfony/symfony/blob/5129c4cf7e294b1a5ea30d6fec6e89b75396dcd2/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToValuesTransformer.php#L74)). (As far as I know, non-multiple and non-expanded form has no problem, thanks to [ChoiceToValueTransformer](https://github.com/symfony/symfony/blob/6babdb3296a5fbbd9c69d1e3410adbdf749959ef/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToValueTransformer.php#L43)) To resolve these problems, I just added a simple-validation listener to choice type. Commits ------- 64d7a82 [Form] Fix ChoiceType to ensure submitted data is not nested unnecessarily
This PR was squashed before being merged into the 2.7 branch (closes #21957). Discussion ---------- [Form] Choice type int values (BC Fix) | Q | A | ------------- | --- | Branch? | 2.7 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #21952 | License | MIT #21267 unnecessarily forced all choice values to be strings, this was a BC braking change. Everything can work fine if they are strings or ints. specifically the issue was because `array_flip` is used on the `choices` array, but this function will work fine with ints as well as strings. Normally, when using HTML forms all data comes as strings, but the `symfony/form` component has use cases beyond this, specifically, I use it with JSON data, where valid values for a `ChoiceType` might not be strings. Commits ------- ed211e9 [Form] Choice type int values (BC Fix)
- symfony 3.2.5/6で、choice typeでint値を受け付けなくなっていたため(symfony/symfony#21267) - 21267はbc break扱いで再度修正が入っている(symfony/symfony#21957) - symfony3.2.7で解消されると思われるが、テスト通したいので直しておく
…y/symfony#21267) - 21267はbc break扱いで再度修正が入っている(symfony/symfony#21957) - symfony3.2.7で解消されると思われるが、テスト通したいので直しておく
} | ||
|
||
foreach ($data as $v) { | ||
if (null !== $v && !is_string($v)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The is_string() check may be a little bit to restrictive in a loosely typed language like PHP.
In our code we have an event listener that adds IDs to the submitted form data. But the IDs are integers. So in Symfony 2.8.17 our processing works fine, but in 2.8.18 our processing fails. So for me the fixed code introduces a BC break. What is your opinion about that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see #21957 which is part of the 2.8.19 release
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks :)
But 2.8.19 has some changes in DoctrineBridge that breaks our code (so we switched back to 2.18.18) :(
Fixed ChoiceType to protect against some problem caused by treating of array.
Let's say we have the choice-form like:
Then, submit data like this:
(Yes, I agree in most cases these situation doesn't happen, but can be)
Then, we get
array_flip(): Can only flip STRING and INTEGER values!
error at here.Even if form is not
multiple
, annoyingArray to string conversion
error occurs in here (via ChoicesToValuesTransformer).(As far as I know, non-multiple and non-expanded form has no problem, thanks to ChoiceToValueTransformer)
To resolve these problems, I just added a simple-validation listener to choice type.