Skip to content

Commit

Permalink
feature #24387 [FORM] Prevent forms from extending itself as a parent…
Browse files Browse the repository at this point in the history
… (pierredup)

This PR was squashed before being merged into the 3.4 branch (closes #24387).

Discussion
----------

[FORM] Prevent forms from extending itself as a parent

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | N/A
| License       | MIT
| Doc PR        | N/A

If a form type defines itself as a parent, it results in an endless recursive loop in the form registry, so throw an exception instead.

Commits
-------

2ee6fd5 [FORM] Prevent forms from extending itself as a parent
  • Loading branch information
nicolas-grekas committed Oct 10, 2017
2 parents d48bcbf + 2ee6fd5 commit f76ea80
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 9 deletions.
32 changes: 23 additions & 9 deletions src/Symfony/Component/Form/FormRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Form;

use Symfony\Component\Form\Exception\ExceptionInterface;
use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Exception\InvalidArgumentException;

Expand Down Expand Up @@ -44,6 +45,8 @@ class FormRegistry implements FormRegistryInterface
*/
private $resolvedTypeFactory;

private $checkedTypes = array();

/**
* @param FormExtensionInterface[] $extensions An array of FormExtensionInterface
* @param ResolvedFormTypeFactoryInterface $resolvedTypeFactory The factory for resolved form types
Expand Down Expand Up @@ -106,18 +109,29 @@ private function resolveType(FormTypeInterface $type)
$parentType = $type->getParent();
$fqcn = get_class($type);

foreach ($this->extensions as $extension) {
$typeExtensions = array_merge(
if (isset($this->checkedTypes[$fqcn])) {
$types = implode(' > ', array_merge(array_keys($this->checkedTypes), array($fqcn)));
throw new LogicException(sprintf('Circular reference detected for form "%s" (%s).', $fqcn, $types));
}

$this->checkedTypes[$fqcn] = true;

try {
foreach ($this->extensions as $extension) {
$typeExtensions = array_merge(
$typeExtensions,
$extension->getTypeExtensions($fqcn)
);
}

return $this->resolvedTypeFactory->createResolvedType(
$type,
$typeExtensions,
$extension->getTypeExtensions($fqcn)
$parentType ? $this->getType($parentType) : null
);
} finally {
unset($this->checkedTypes[$fqcn]);
}

return $this->resolvedTypeFactory->createResolvedType(
$type,
$typeExtensions,
$parentType ? $this->getType($parentType) : null
);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Form\Tests\Fixtures;

use Symfony\Component\Form\AbstractType;

class FormWithSameParentType extends AbstractType
{
public function getParent()
{
return self::class;
}
}
22 changes: 22 additions & 0 deletions src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeBar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Form\Tests\Fixtures;

use Symfony\Component\Form\AbstractType;

class RecursiveFormTypeBar extends AbstractType
{
public function getParent()
{
return RecursiveFormTypeBaz::class;
}
}
22 changes: 22 additions & 0 deletions src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeBaz.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Form\Tests\Fixtures;

use Symfony\Component\Form\AbstractType;

class RecursiveFormTypeBaz extends AbstractType
{
public function getParent()
{
return RecursiveFormTypeFoo::class;
}
}
22 changes: 22 additions & 0 deletions src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeFoo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Form\Tests\Fixtures;

use Symfony\Component\Form\AbstractType;

class RecursiveFormTypeFoo extends AbstractType
{
public function getParent()
{
return RecursiveFormTypeBar::class;
}
}
34 changes: 34 additions & 0 deletions src/Symfony/Component/Form/Tests/FormRegistryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
use Symfony\Component\Form\FormTypeGuesserChain;
use Symfony\Component\Form\ResolvedFormType;
use Symfony\Component\Form\ResolvedFormTypeFactoryInterface;
use Symfony\Component\Form\Tests\Fixtures\FormWithSameParentType;
use Symfony\Component\Form\Tests\Fixtures\RecursiveFormTypeBar;
use Symfony\Component\Form\Tests\Fixtures\RecursiveFormTypeBaz;
use Symfony\Component\Form\Tests\Fixtures\RecursiveFormTypeFoo;
use Symfony\Component\Form\Tests\Fixtures\FooSubType;
use Symfony\Component\Form\Tests\Fixtures\FooType;
use Symfony\Component\Form\Tests\Fixtures\FooTypeBarExtension;
Expand Down Expand Up @@ -156,6 +160,36 @@ public function testGetTypeConnectsParent()
$this->assertSame($resolvedType, $this->registry->getType(get_class($type)));
}

/**
* @expectedException \Symfony\Component\Form\Exception\LogicException
* @expectedExceptionMessage Circular reference detected for form "Symfony\Component\Form\Tests\Fixtures\FormWithSameParentType" (Symfony\Component\Form\Tests\Fixtures\FormWithSameParentType > Symfony\Component\Form\Tests\Fixtures\FormWithSameParentType).
*/
public function testFormCannotHaveItselfAsAParent()
{
$type = new FormWithSameParentType();

$this->extension2->addType($type);

$this->registry->getType(FormWithSameParentType::class);
}

/**
* @expectedException \Symfony\Component\Form\Exception\LogicException
* @expectedExceptionMessage Circular reference detected for form "Symfony\Component\Form\Tests\Fixtures\RecursiveFormTypeFoo" (Symfony\Component\Form\Tests\Fixtures\RecursiveFormTypeFoo > Symfony\Component\Form\Tests\Fixtures\RecursiveFormTypeBar > Symfony\Component\Form\Tests\Fixtures\RecursiveFormTypeBaz > Symfony\Component\Form\Tests\Fixtures\RecursiveFormTypeFoo).
*/
public function testRecursiveFormDependencies()
{
$foo = new RecursiveFormTypeFoo();
$bar = new RecursiveFormTypeBar();
$baz = new RecursiveFormTypeBaz();

$this->extension2->addType($foo);
$this->extension2->addType($bar);
$this->extension2->addType($baz);

$this->registry->getType(RecursiveFormTypeFoo::class);
}

/**
* @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException
*/
Expand Down

0 comments on commit f76ea80

Please sign in to comment.