Thanks to visit codestin.com
Credit goes to github.com

Skip to content

FormInterface is reported as \Traversable<string, FormInterface> but can return int as key. #46698

Closed
@VincentLanglet

Description

@VincentLanglet

Symfony version(s) affected

5.0.0 and more

Description

Because the formBuilder use name as key, and because php is casting int-string like '0' to int, we sometimes ends with form name which is integer and this cannot be directly used by method like FormInterface::remove.

How to reproduce

The following code

foreach ($form as $name => $child) {
     $form->remove($name);
}

is not working when the form was build this way

$formBuilder->add('0', ...)

Because the name is casted to an int when used as an array key here:
https://github.com/symfony/symfony/blob/6.2/src/Symfony/Component/Form/FormBuilder.php#L71-L72

There are some situation in the Symfony code where the form names can be int and passed to method expecting string. One of them is the ChoiceType with expanded option.
https://github.com/symfony/form/blob/6.1/Extension/Core/Type/ChoiceType.php#L407-L419

So far this code is working because declare(strict_types=1); is not used.

But for my first example

foreach ($form as $name => $child) {
     $form->remove($name);
}

if it's written in the userland with strict_types, it won't work even if all the phpdoc make believe it will:

  • FormInterface is a \Traversable<string, FormInterface>
  • remove accepts string

Possible Solution

I see at least four solutions here:

  • Changing some of the typehint in https://github.com/symfony/symfony/blob/6.1/src/Symfony/Component/Form/FormInterface.php to accepts int|string (since it's a BC-break, this might wait for 7.0)
  • Changing the phpdoc to \Traversable<int|string, FormInterface> with some comments; at least static-analysis user will be warned.
  • Deprecating passing an integer-as-string as a form name ; this might be the smoother solution. Instead of $formBuilder->add('0') it should be $formBuilder->add('prefix_0'). And in SF 7.0, this will throw an exception.
  • Doing nothing

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions