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;
|
||||
|
||||
use Symfony\Component\Form\Exception\ExceptionInterface;
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Form\Exception\InvalidArgumentException;
|
||||
|
||||
@ -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
|
||||
@ -106,18 +109,29 @@ class FormRegistry implements FormRegistryInterface
|
||||
$parentType = $type->getParent();
|
||||
$fqcn = get_class($type);
|
||||
|
||||
foreach ($this->extensions as $extension) {
|
||||
$typeExtensions = array_merge(
|
||||
$typeExtensions,
|
||||
$extension->getTypeExtensions($fqcn)
|
||||
);
|
||||
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));
|
||||
}
|
||||
|
||||
return $this->resolvedTypeFactory->createResolvedType(
|
||||
$type,
|
||||
$typeExtensions,
|
||||
$parentType ? $this->getType($parentType) : null
|
||||
);
|
||||
$this->checkedTypes[$fqcn] = true;
|
||||
|
||||
try {
|
||||
foreach ($this->extensions as $extension) {
|
||||
$typeExtensions = array_merge(
|
||||
$typeExtensions,
|
||||
$extension->getTypeExtensions($fqcn)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->resolvedTypeFactory->createResolvedType(
|
||||
$type,
|
||||
$typeExtensions,
|
||||
$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\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;
|
||||
@ -156,6 +160,36 @@ class FormRegistryTest extends TestCase
|
||||
$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
|
||||
*/
|
||||
|
Reference in New Issue
Block a user