Skip to content

Commit 6e43dc0

Browse files
pierredupnicolas-grekas
authored andcommitted
[FORM] Prevent forms from extending itself as a parent
1 parent ea330e3 commit 6e43dc0

File tree

6 files changed

+145
-9
lines changed

6 files changed

+145
-9
lines changed

FormRegistry.php

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Form;
1313

1414
use Symfony\Component\Form\Exception\ExceptionInterface;
15+
use Symfony\Component\Form\Exception\LogicException;
1516
use Symfony\Component\Form\Exception\UnexpectedTypeException;
1617
use Symfony\Component\Form\Exception\InvalidArgumentException;
1718

@@ -44,6 +45,8 @@ class FormRegistry implements FormRegistryInterface
4445
*/
4546
private $resolvedTypeFactory;
4647

48+
private $checkedTypes = array();
49+
4750
/**
4851
* @param FormExtensionInterface[] $extensions An array of FormExtensionInterface
4952
* @param ResolvedFormTypeFactoryInterface $resolvedTypeFactory The factory for resolved form types
@@ -106,18 +109,29 @@ private function resolveType(FormTypeInterface $type)
106109
$parentType = $type->getParent();
107110
$fqcn = get_class($type);
108111

109-
foreach ($this->extensions as $extension) {
110-
$typeExtensions = array_merge(
112+
if (isset($this->checkedTypes[$fqcn])) {
113+
$types = implode(' > ', array_merge(array_keys($this->checkedTypes), array($fqcn)));
114+
throw new LogicException(sprintf('Circular reference detected for form "%s" (%s).', $fqcn, $types));
115+
}
116+
117+
$this->checkedTypes[$fqcn] = true;
118+
119+
try {
120+
foreach ($this->extensions as $extension) {
121+
$typeExtensions = array_merge(
122+
$typeExtensions,
123+
$extension->getTypeExtensions($fqcn)
124+
);
125+
}
126+
127+
return $this->resolvedTypeFactory->createResolvedType(
128+
$type,
111129
$typeExtensions,
112-
$extension->getTypeExtensions($fqcn)
130+
$parentType ? $this->getType($parentType) : null
113131
);
132+
} finally {
133+
unset($this->checkedTypes[$fqcn]);
114134
}
115-
116-
return $this->resolvedTypeFactory->createResolvedType(
117-
$type,
118-
$typeExtensions,
119-
$parentType ? $this->getType($parentType) : null
120-
);
121135
}
122136

123137
/**
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Tests\Fixtures;
13+
14+
use Symfony\Component\Form\AbstractType;
15+
16+
class FormWithSameParentType extends AbstractType
17+
{
18+
public function getParent()
19+
{
20+
return self::class;
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Tests\Fixtures;
13+
14+
use Symfony\Component\Form\AbstractType;
15+
16+
class RecursiveFormTypeBar extends AbstractType
17+
{
18+
public function getParent()
19+
{
20+
return RecursiveFormTypeBaz::class;
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Tests\Fixtures;
13+
14+
use Symfony\Component\Form\AbstractType;
15+
16+
class RecursiveFormTypeBaz extends AbstractType
17+
{
18+
public function getParent()
19+
{
20+
return RecursiveFormTypeFoo::class;
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Tests\Fixtures;
13+
14+
use Symfony\Component\Form\AbstractType;
15+
16+
class RecursiveFormTypeFoo extends AbstractType
17+
{
18+
public function getParent()
19+
{
20+
return RecursiveFormTypeBar::class;
21+
}
22+
}

Tests/FormRegistryTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
use Symfony\Component\Form\FormTypeGuesserChain;
1717
use Symfony\Component\Form\ResolvedFormType;
1818
use Symfony\Component\Form\ResolvedFormTypeFactoryInterface;
19+
use Symfony\Component\Form\Tests\Fixtures\FormWithSameParentType;
20+
use Symfony\Component\Form\Tests\Fixtures\RecursiveFormTypeBar;
21+
use Symfony\Component\Form\Tests\Fixtures\RecursiveFormTypeBaz;
22+
use Symfony\Component\Form\Tests\Fixtures\RecursiveFormTypeFoo;
1923
use Symfony\Component\Form\Tests\Fixtures\FooSubType;
2024
use Symfony\Component\Form\Tests\Fixtures\FooType;
2125
use Symfony\Component\Form\Tests\Fixtures\FooTypeBarExtension;
@@ -156,6 +160,36 @@ public function testGetTypeConnectsParent()
156160
$this->assertSame($resolvedType, $this->registry->getType(get_class($type)));
157161
}
158162

163+
/**
164+
* @expectedException \Symfony\Component\Form\Exception\LogicException
165+
* @expectedExceptionMessage Circular reference detected for form "Symfony\Component\Form\Tests\Fixtures\FormWithSameParentType" (Symfony\Component\Form\Tests\Fixtures\FormWithSameParentType > Symfony\Component\Form\Tests\Fixtures\FormWithSameParentType).
166+
*/
167+
public function testFormCannotHaveItselfAsAParent()
168+
{
169+
$type = new FormWithSameParentType();
170+
171+
$this->extension2->addType($type);
172+
173+
$this->registry->getType(FormWithSameParentType::class);
174+
}
175+
176+
/**
177+
* @expectedException \Symfony\Component\Form\Exception\LogicException
178+
* @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).
179+
*/
180+
public function testRecursiveFormDependencies()
181+
{
182+
$foo = new RecursiveFormTypeFoo();
183+
$bar = new RecursiveFormTypeBar();
184+
$baz = new RecursiveFormTypeBaz();
185+
186+
$this->extension2->addType($foo);
187+
$this->extension2->addType($bar);
188+
$this->extension2->addType($baz);
189+
190+
$this->registry->getType(RecursiveFormTypeFoo::class);
191+
}
192+
159193
/**
160194
* @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException
161195
*/

0 commit comments

Comments
 (0)