feature #24387 [FORM] Prevent forms from extending itself as a parent (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
This commit is contained in:
commit
f76ea8016a
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\Form;
|
namespace Symfony\Component\Form;
|
||||||
|
|
||||||
use Symfony\Component\Form\Exception\ExceptionInterface;
|
use Symfony\Component\Form\Exception\ExceptionInterface;
|
||||||
|
use Symfony\Component\Form\Exception\LogicException;
|
||||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||||
use Symfony\Component\Form\Exception\InvalidArgumentException;
|
use Symfony\Component\Form\Exception\InvalidArgumentException;
|
||||||
|
|
||||||
@ -44,6 +45,8 @@ class FormRegistry implements FormRegistryInterface
|
|||||||
*/
|
*/
|
||||||
private $resolvedTypeFactory;
|
private $resolvedTypeFactory;
|
||||||
|
|
||||||
|
private $checkedTypes = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param FormExtensionInterface[] $extensions An array of FormExtensionInterface
|
* @param FormExtensionInterface[] $extensions An array of FormExtensionInterface
|
||||||
* @param ResolvedFormTypeFactoryInterface $resolvedTypeFactory The factory for resolved form types
|
* @param ResolvedFormTypeFactoryInterface $resolvedTypeFactory The factory for resolved form types
|
||||||
@ -106,6 +109,14 @@ class FormRegistry implements FormRegistryInterface
|
|||||||
$parentType = $type->getParent();
|
$parentType = $type->getParent();
|
||||||
$fqcn = get_class($type);
|
$fqcn = get_class($type);
|
||||||
|
|
||||||
|
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) {
|
foreach ($this->extensions as $extension) {
|
||||||
$typeExtensions = array_merge(
|
$typeExtensions = array_merge(
|
||||||
$typeExtensions,
|
$typeExtensions,
|
||||||
@ -118,6 +129,9 @@ class FormRegistry implements FormRegistryInterface
|
|||||||
$typeExtensions,
|
$typeExtensions,
|
||||||
$parentType ? $this->getType($parentType) : null
|
$parentType ? $this->getType($parentType) : null
|
||||||
);
|
);
|
||||||
|
} finally {
|
||||||
|
unset($this->checkedTypes[$fqcn]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,10 @@ use Symfony\Component\Form\FormRegistry;
|
|||||||
use Symfony\Component\Form\FormTypeGuesserChain;
|
use Symfony\Component\Form\FormTypeGuesserChain;
|
||||||
use Symfony\Component\Form\ResolvedFormType;
|
use Symfony\Component\Form\ResolvedFormType;
|
||||||
use Symfony\Component\Form\ResolvedFormTypeFactoryInterface;
|
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\FooSubType;
|
||||||
use Symfony\Component\Form\Tests\Fixtures\FooType;
|
use Symfony\Component\Form\Tests\Fixtures\FooType;
|
||||||
use Symfony\Component\Form\Tests\Fixtures\FooTypeBarExtension;
|
use Symfony\Component\Form\Tests\Fixtures\FooTypeBarExtension;
|
||||||
@ -156,6 +160,36 @@ class FormRegistryTest extends TestCase
|
|||||||
$this->assertSame($resolvedType, $this->registry->getType(get_class($type)));
|
$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
|
* @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user