From 2ee6fd54e19437fb8893e10f7a8c43f1709c6512 Mon Sep 17 00:00:00 2001 From: Pierre du Plessis Date: Sun, 1 Oct 2017 10:07:08 +0200 Subject: [PATCH] [FORM] Prevent forms from extending itself as a parent --- src/Symfony/Component/Form/FormRegistry.php | 34 +++++++++++++------ .../Tests/Fixtures/FormWithSameParentType.php | 22 ++++++++++++ .../Tests/Fixtures/RecursiveFormTypeBar.php | 22 ++++++++++++ .../Tests/Fixtures/RecursiveFormTypeBaz.php | 22 ++++++++++++ .../Tests/Fixtures/RecursiveFormTypeFoo.php | 22 ++++++++++++ .../Component/Form/Tests/FormRegistryTest.php | 34 +++++++++++++++++++ 6 files changed, 146 insertions(+), 10 deletions(-) create mode 100644 src/Symfony/Component/Form/Tests/Fixtures/FormWithSameParentType.php create mode 100644 src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeBar.php create mode 100644 src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeBaz.php create mode 100644 src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeFoo.php diff --git a/src/Symfony/Component/Form/FormRegistry.php b/src/Symfony/Component/Form/FormRegistry.php index 6e00755378..c81d45957b 100644 --- a/src/Symfony/Component/Form/FormRegistry.php +++ b/src/Symfony/Component/Form/FormRegistry.php @@ -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]); + } } /** diff --git a/src/Symfony/Component/Form/Tests/Fixtures/FormWithSameParentType.php b/src/Symfony/Component/Form/Tests/Fixtures/FormWithSameParentType.php new file mode 100644 index 0000000000..098f9a2a2b --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Fixtures/FormWithSameParentType.php @@ -0,0 +1,22 @@ + + * + * 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; + } +} diff --git a/src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeBar.php b/src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeBar.php new file mode 100644 index 0000000000..a7b0d4df35 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeBar.php @@ -0,0 +1,22 @@ + + * + * 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; + } +} diff --git a/src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeBaz.php b/src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeBaz.php new file mode 100644 index 0000000000..63f62e757f --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeBaz.php @@ -0,0 +1,22 @@ + + * + * 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; + } +} diff --git a/src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeFoo.php b/src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeFoo.php new file mode 100644 index 0000000000..a41f63ee0b --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Fixtures/RecursiveFormTypeFoo.php @@ -0,0 +1,22 @@ + + * + * 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; + } +} diff --git a/src/Symfony/Component/Form/Tests/FormRegistryTest.php b/src/Symfony/Component/Form/Tests/FormRegistryTest.php index 5deddff422..4fbcb4339a 100644 --- a/src/Symfony/Component/Form/Tests/FormRegistryTest.php +++ b/src/Symfony/Component/Form/Tests/FormRegistryTest.php @@ -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 */