8000 [Form] Deprecate passing ints by vudaltsov · Pull Request #32821 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Form] Deprecate passing ints #32821

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

Closed
wants to merge 2 commits into from
Closed
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 src/Symfony/Component/Form/ButtonBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ public function __construct(?string $name, array $options = [])
*
* This method should not be invoked.
*
* @param string|int|FormBuilderInterface $child
Copy link
Member
@nicolas-grekas nicolas-grekas Jul 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All similar changes make sense, but I'm not sure the rest does: passing an int will just work in Symfony 5, thanks to the string type. Ppl that use strict mode won't have a smooth upgrade path that's true, but are we going to ask everyone to make their code "strict" while only some actually need it? Not sure...

Copy link
Contributor
@Tobion Tobion Jul 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that we should rely on auto casting internally. No need to forbid integers passed from users. See #32066
I don't see what these changes try to accomplish.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Tobion , these changes are for the ones calling $form->add(1) from classes with strict types enabled. Once we add string type in 5.0, they will have an error.

But now I see the point of @nicolas-grekas better (we already discussed that earlier): if a user does declare(strict_types=1), it's her responsibility to check that she passes correct arguments, because Symfony is not strict about types.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strict types is a bad idea, you have a perfect example of why.
It provides no additional type-guarantee.
On the contrary, type declarations on signature boundaries do provide additional guarantee and thus prevent bugs.
Good news, we're adding them all in v5 when technically possible :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #32828 as alternative.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolas-grekas , you mean 1? :)
@xabbuh, so, do you agree that we should close this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean that this situation will happen to all places where we added the string type - not only on forms.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have only done that in places where we explicitly declared support for other data types before. But I also understand the argument about everyone having to do the type cast then (though I wouldn't expect that many people actually use integers as form names).

Copy link
Contributor Author
@vudaltsov vudaltsov Jul 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolas-grekas , yes, I got it. Should we add a note in the BC promise about that then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I meant 1. :)
The BC promise is only about minor versions, so that's a bit off topic, but I don't have a better proposal.
PR welcome on the BC promise then, to be clear we don't promise not hard-breaking strict types.

* @param string|FormTypeInterface $type
* @param array $options
* @param string|FormBuilderInterface $child
* @param string|FormTypeInterface $type
* @param array $options
*
* @throws BadMethodCallException
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public function preSetData(FormEvent $event)

// Then add all rows again in the correct order
foreach ($data as $name => $value) {
$form->add($name, $this->type, array_replace([
$form->add((string) $name, $this->type, array_replace([
'property_path' => '['.$name.']',
], $this->options));
}
Expand Down Expand Up @@ -105,7 +105,7 @@ public function preSubmit(FormEvent $event)
if ($this->allowAdd) {
foreach ($data as $name => $value) {
if (!$form->has($name)) {
$form->add($name, $this->type, array_replace([
$form->add((string) $name, $this->type, array_replace([
'property_path' => '['.$name.']',
], $this->options));
}
Expand Down
9 changes: 7 additions & 2 deletions src/Symfony/Component/Form/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -843,8 +843,13 @@ public function add($child, $type = null, array $options = [])
}

if (!$child instanceof FormInterface) {
if (!\is_string($child) && !\is_int($child)) {
throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormInterface');
if (\is_int($child)) {
@trigger_error(sprintf('Passing an integer child name to "%s" is deprecated since Symfony 4.4. Pass a string instead.', __METHOD__), \E_USER_DEPRECATED);
$child = (string) $child;
}

if (!\is_string($child)) {
throw new UnexpectedTypeException($child, 'string or Symfony\Component\Form\FormInterface');
}

$child = (string) $child;
Expand Down
28 changes: 25 additions & 3 deletions src/Symfony/Component/Form/FormBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,13 @@ public function add($child, $type = null, array $options = [])
return $this;
}

if (!\is_string($child) && !\is_int($child)) {
throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormBuilderInterface');
if (\is_int($child)) {
@trigger_error(sprintf('Passing an integer child name to "%s" is deprecated since Symfony 4.4. Pass a string instead.', __METHOD__), \E_USER_DEPRECATED);
$child = (string) $child;
}

if (!\is_string($child)) {
throw new UnexpectedTypeException($child, 'string or Symfony\Component\Form\FormBuilderInterface');
}

if (null !== $type && !\is_string($type) && !$type instanceof FormTypeInterface) {
Expand All @@ -86,6 +91,11 @@ public function create($name, $type = null, array $options = [])
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}

if (\is_int($name)) {
@trigger_error(sprintf('Passing an integer name to "%s" is deprecated since Symfony 4.4. Pass a string instead.', __METHOD__), \E_USER_DEPRECATED);
$name = (string) $name;
}

if (null === $type && null === $this->getDataClass()) {
$type = 'Symfony\Component\Form\Extension\Core\Type\TextType';
}
Expand All @@ -106,6 +116,10 @@ public function get($name)
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}

if (\is_int($name)) {
@trigger_error(sprintf('Passing an integer name to "%s" is deprecated since Symfony 4.4. Pass a string instead.', __METHOD__), \E_USER_DEPRECATED);
}

if (isset($this->unresolvedChildren[$name])) {
return $this->resolveChild($name);
}
Expand All @@ -126,6 +140,10 @@ public function remove($name)
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}

if (\is_int($name)) {
@trigger_error(sprintf('Passing an integer name to "%s" is deprecated since Symfony 4.4. Pass a string instead.', __METHOD__), \E_USER_DEPRECATED);
}

unset($this->unresolvedChildren[$name], $this->children[$name]);

return $this;
Expand All @@ -140,6 +158,10 @@ public function has($name)
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}

if (\is_int($name)) {
@trigger_error(sprintf('Passing an integer name to "%s" is deprecated since Symfony 4.4. Pass a string instead.', __METHOD__), \E_USER_DEPRECATED);
}

return isset($this->unresolvedChildren[$name]) || isset($this->children[$name]);
}

Expand Down Expand Up @@ -241,7 +263,7 @@ private function resolveChild(string $name): FormBuilderInterface
private function resolveChildren()
{
foreach ($this->unresolvedChildren as $name => $info) {
$this->children[$name] = $this->create($name, $info[0], $info[1]);
$this->children[$name] = $this->create((string) $name, $info[0], $info[1]);
}

$this->unresolvedChildren = [];
Expand Down
6 changes: 3 additions & 3 deletions src/Symfony/Component/Form/FormBuilderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ interface FormBuilderInterface extends \Traversable, \Countable, FormConfigBuild
* If you add a nested group, this group should also be represented in the
* object hierarchy.
*
* @param string|int|FormBuilderInterface $child
* @param string|null $type
* @param array $options
* @param string|FormBuilderInterface $child
* @param string|null $type
* @param array $options
*
* @return self
*/
Expand Down
5 changes: 5 additions & 0 deletions src/Symfony/Component/Form/FormConfigBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,11 @@ public function getFormConfig()
*/
public static function validateName($name)
{
if (\is_int($name)) {
@trigger_error(sprintf('Passing an integer name to "%s" is deprecated since Symfony 4.4. Pass a string instead.', __METHOD__), \E_USER_DEPRECATED);
$name = (string) $name;
}

if (null !== $name && !\is_string($name)) {
throw new UnexpectedTypeException($name, 'string or null');
}
Expand Down
10 changes: 10 additions & 0 deletions src/Symfony/Component/Form/FormFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public function create($type = 'Symfony\Component\Form\Extension\Core\Type\FormT
*/
public function createNamed($name, $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = [])
{
if (\is_int($name)) {
@trigger_error(sprintf('Passing an integer name to "%s" is deprecated since Symfony 4.4. Pass a string instead.', __METHOD__), \E_USER_DEPRECATED);
$name = (string) $name;
}

return $this->createNamedBuilder($name, $type, $data, $options)->getForm();
}

Expand Down Expand Up @@ -63,6 +68,11 @@ public function createBuilder($type = 'Symfony\Component\Form\Extension\Core\Typ
*/
public function createNamedBuilder($name, $type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = [])
{
if (\is_int($name)) {
@trigger_error(sprintf('Passing an integer name to "%s" is deprecated since Symfony 4.4. Pass a string instead.', __METHOD__), \E_USER_DEPRECATED);
$name = (string) $name;
}

if (null !== $data && !\array_key_exists('data', $options)) {
$options['data'] = $data;
}
Expand Down
16 changes: 8 additions & 8 deletions src/Symfony/Component/Form/FormFactoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ public function create($type = 'Symfony\Component\Form\Extension\Core\Type\FormT
*
* @see createNamedBuilder()
*
* @param string|int $name The name of the form
* @param string $type The type of the form
* @param mixed $data The initial data
* @param array $options The options
* @param string $name The name of the form
* @param string $type The type of the form
* @param mixed $data The initial data
* @param array $options The options
*
* @return FormInterface The form
*
Expand Down Expand Up @@ -81,10 +81,10 @@ public function createBuilder($type = 'Symfony\Component\Form\Extension\Core\Typ
/**
* Returns a form builder.
*
* @param string|int $name The name of the form
* @param string $type The type of the form
* @param mixed $data The initial data
* @param array $options The options
* @param string $name The name of the form
* @param string $type The type of the form
* @param mixed $data The initial data
* @param array $options The options
*
* @return FormBuilderInterface The form builder
*
Expand Down
6 changes: 3 additions & 3 deletions src/Symfony/Component/Form/FormInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ public function getParent();
/**
* Adds or replaces a child to the form.
*
* @param FormInterface|string|int $child The FormInterface instance or the name of the child
* @param string|null $type The child's type, if a name was passed
* @param array $options The child's options, if a name was passed
* @param FormInterface|string $child The FormInterface instance or the name of the child
* @param string|null $type The child's type, if a name was passed
* @param array $options The child's options, if a name was passed
*
* @return $this
*
Expand Down
10 changes: 10 additions & 0 deletions src/Symfony/Component/Form/ResolvedFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ public function getTypeExtensions()
*/
public function createBuilder(FormFactoryInterface $factory, $name, array $options = [])
{
if (\is_int($name)) {
@trigger_error(sprintf('Passing an integer name to "%s" is deprecated since Symfony 4.4. Pass a string instead.', __METHOD__), \E_USER_DEPRECATED);
$name = (string) $name;
}

try {
$options = $this->getOptionsResolver()->resolve($options);
} catch (ExceptionInterface $e) {
Expand Down Expand Up @@ -218,6 +223,11 @@ public function getOptionsResolver()
*/
protected function newBuilder($name, $dataClass, FormFactoryInterface $factory, array $options)
{
if (\is_int($name)) {
@trigger_error(sprintf('Passing an integer name to "%s" is deprecated since Symfony 4.4. Pass a string instead.', __METHOD__), \E_USER_DEPRECATED);
$name = (string) $name;
}

if ($this->innerType instanceof ButtonTypeInterface) {
return new ButtonBuilder($name, $options);
}
Expand Down
3 changes: 3 additions & 0 deletions src/Symfony/Component/Form/Tests/CompoundFormTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ public function testAddUsingNameAndType()
$this->assertSame(['foo' => $child], $this->form->all());
}

/**
* @group legacy
*/
public function testAddUsingIntegerNameAndType()
{
$child = $this->getBuilder(0)->getForm();
Expand Down
3 changes: 3 additions & 0 deletions src/Symfony/Component/Form/Tests/FormBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ public function testAdd()
$this->assertTrue($this->builder->has('foo'));
}

/**
* @group legacy
*/
public function testAddIntegerName()
{
$this->assertFalse($this->builder->has(0));
Expand Down
0