Skip to content

Commit 169e965

Browse files
committed
Validate the extended type for lazy-loaded type extensions
1 parent cecc2ee commit 169e965

File tree

4 files changed

+138
-42
lines changed

4 files changed

+138
-42
lines changed

UPGRADE-2.8.md

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Form
2121

2222
```php
2323
use Symfony\Component\Validator\Constraints\Valid;
24-
24+
2525
$form = $this->createFormBuilder($article)
2626
->add('author', new AuthorType(), array(
2727
'constraints' => new Valid(),
@@ -42,42 +42,42 @@ Form
4242
private $author;
4343
}
4444
```
45-
45+
4646
* Type names were deprecated and will be removed in Symfony 3.0. Instead of
4747
referencing types by name, you should reference them by their
4848
fully-qualified class name (FQCN) instead. With PHP 5.5 or later, you can
4949
use the "class" constant for that:
50-
50+
5151
Before:
52-
52+
5353
```php
5454
$form = $this->createFormBuilder()
5555
->add('name', 'text')
5656
->add('age', 'integer')
5757
->getForm();
5858
```
59-
59+
6060
After:
61-
61+
6262
```php
6363
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
6464
use Symfony\Component\Form\Extension\Core\Type\TextType;
65-
65+
6666
$form = $this->createFormBuilder()
6767
->add('name', TextType::class)
6868
->add('age', IntegerType::class)
6969
->getForm();
7070
```
71-
71+
7272
As a further consequence, the method `FormTypeInterface::getName()` was
7373
deprecated and will be removed in Symfony 3.0. You should remove this method
7474
from your form types.
75-
75+
7676
If you want to customize the block prefix of a type in Twig, you should now
7777
implement `FormTypeInterface::getBlockPrefix()` instead:
78-
78+
7979
Before:
80-
80+
8181
```php
8282
class UserProfileType extends AbstractType
8383
{
@@ -87,9 +87,9 @@ Form
8787
}
8888
}
8989
```
90-
90+
9191
After:
92-
92+
9393
```php
9494
class UserProfileType extends AbstractType
9595
{
@@ -99,53 +99,53 @@ Form
9999
}
100100
}
101101
```
102-
102+
103103
If you don't customize `getBlockPrefix()`, it defaults to the class name
104104
without "Type" suffix in underscore notation (here: "user_profile").
105-
105+
106106
If you want to create types that are compatible with Symfony 2.3 up to 2.8
107107
and don't trigger deprecation errors, implement *both* `getName()` and
108108
`getBlockPrefix()`:
109-
109+
110110
```php
111111
class ProfileType extends AbstractType
112112
{
113113
public function getName()
114114
{
115115
return $this->getBlockPrefix();
116116
}
117-
117+
118118
public function getBlockPrefix()
119119
{
120120
return 'profile';
121121
}
122122
}
123123
```
124-
124+
125125
If you define your form types in the Dependency Injection configuration, you
126126
should further remove the "alias" attribute:
127-
127+
128128
Before:
129-
129+
130130
```xml
131131
<service id="my.type" class="Vendor\Type\MyType">
132132
<tag name="form.type" alias="mytype" />
133133
</service>
134134
```
135-
135+
136136
After:
137-
137+
138138
```xml
139139
<service id="my.type" class="Vendor\Type\MyType">
140140
<tag name="form.type" />
141141
</service>
142142
```
143-
143+
144144
Type extension should return the fully-qualified class name of the extended
145145
type from `FormTypeExtensionInterface::getExtendedType()` now.
146-
146+
147147
Before:
148-
148+
149149
```php
150150
class MyTypeExtension extends AbstractTypeExtension
151151
{
@@ -155,12 +155,12 @@ Form
155155
}
156156
}
157157
```
158-
158+
159159
After:
160-
160+
161161
```php
162162
use Symfony\Component\Form\Extension\Core\Type\FormType;
163-
163+
164164
class MyTypeExtension extends AbstractTypeExtension
165165
{
166166
public function getExtendedType()
@@ -169,14 +169,14 @@ Form
169169
}
170170
}
171171
```
172-
172+
173173
If your extension has to be compatible with Symfony 2.3-2.8, use the
174174
following statement:
175-
175+
176176
```php
177177
use Symfony\Component\Form\AbstractType;
178178
use Symfony\Component\Form\Extension\Core\Type\FormType;
179-
179+
180180
class MyTypeExtension extends AbstractTypeExtension
181181
{
182182
public function getExtendedType()
@@ -185,13 +185,13 @@ Form
185185
}
186186
}
187187
```
188-
188+
189189
* Returning type instances from `FormTypeInterface::getParent()` is deprecated
190190
and will not be supported anymore in Symfony 3.0. Return the fully-qualified
191191
class name of the parent type class instead.
192-
192+
193193
Before:
194-
194+
195195
```php
196196
class MyType
197197
{
@@ -201,9 +201,9 @@ Form
201201
}
202202
}
203203
```
204-
204+
205205
After:
206-
206+
207207
```php
208208
class MyType
209209
{
@@ -213,24 +213,28 @@ Form
213213
}
214214
}
215215
```
216-
216+
217217
* Passing type instances to `Form::add()`, `FormBuilder::add()` and the
218218
`FormFactory::create*()` methods is deprecated and will not be supported
219219
anymore in Symfony 3.0. Pass the fully-qualified class name of the type
220220
instead.
221-
221+
222222
Before:
223-
223+
224224
```php
225225
$form = $this->createForm(new MyType());
226226
```
227-
227+
228228
After:
229-
229+
230230
```php
231231
$form = $this->createForm(MyType::class);
232232
```
233233

234+
* Registering type extensions as a service with an alias which does not
235+
match the type returned by `getExtendedType` is now forbidden. Fix your
236+
implementation to define the right type.
237+
234238
Translator
235239
----------
236240

src/Symfony/Component/Form/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* deprecated the "cascade_validation" option in favor of setting "constraints"
1010
with the Valid constraint
1111
* moved data trimming logic of TrimListener into StringUtil
12+
* [BC BREAK] When registering a type extension through the DI extension, the tag alias has to match the actual extended type.
1213

1314
2.7.0
1415
-----

src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,18 @@ public function getTypeExtensions($name)
8686

8787
if (isset($this->typeExtensionServiceIds[$name])) {
8888
foreach ($this->typeExtensionServiceIds[$name] as $serviceId) {
89-
$extensions[] = $this->container->get($serviceId);
89+
$extensions[] = $extension = $this->container->get($serviceId);
90+
91+
// validate result of getExtendedType() to ensure it is consistent with the service definition
92+
if ($extension->getExtendedType() !== $name) {
93+
throw new InvalidArgumentException(
94+
sprintf('The extended type specified for the service "%s" does not match the actual extended type. Expected "%s", given "%s"',
95+
$serviceId,
96+
$name,
97+
$extension->getExtendedType()
98+
)
99+
);
100+
}
90101
}
91102
}
92103

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Extension\DependencyInjection;
13+
14+
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
15+
use Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension;
16+
17+
class DependencyInjectionExtensionTest extends \PHPUnit_Framework_TestCase
18+
{
19+
public function testGetTypeExtensions()
20+
{
21+
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
22+
23+
$typeExtension1 = $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface');
24+
$typeExtension1->expects($this->any())
25+
->method('getExtendedType')
26+
->willReturn('test');
27+
$typeExtension2 = $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface');
28+
$typeExtension2->expects($this->any())
29+
->method('getExtendedType')
30+
->willReturn('test');
31+
$typeExtension3 = $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface');
32+
$typeExtension3->expects($this->any())
33+
->method('getExtendedType')
34+
->willReturn('other');
35+
36+
$services = array(
37+
'extension1' => $typeExtension1,
38+
'extension2' => $typeExtension2,
39+
'extension3' => $typeExtension3,
40+
);
41+
42+
$container->expects($this->any())
43+
->method('get')
44+
->willReturnCallback(function ($id) use ($services) {
45+
if (isset($services[$id])) {
46+
return $services[$id];
47+
}
48+
49+
throw new ServiceNotFoundException($id);
50+
});
51+
52+
$extension = new DependencyInjectionExtension($container, array(), array('test' => array('extension1', 'extension2'), 'other' => array('extension3')), array());
53+
54+
$this->assertTrue($extension->hasTypeExtensions('test'));
55+
$this->assertFalse($extension->hasTypeExtensions('unknown'));
56+
$this->assertSame(array($typeExtension1, $typeExtension2), $extension->getTypeExtensions('test'));
57+
}
58+
59+
/**
60+
* @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException
61+
*/
62+
public function testThrowExceptionForInvalidExtendedType()
63+
{
64+
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
65+
66+
$typeExtension = $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface');
67+
$typeExtension->expects($this->any())
68+
->method('getExtendedType')
69+
->willReturn('unmatched');
70+
71+
$container->expects($this->any())
72+
->method('get')
73+
->with('extension')
74+
->willReturn($typeExtension);
75+
76+
$extension = new DependencyInjectionExtension($container, array(), array('test' => array('extension')), array());
77+
78+
$extension->getTypeExtensions('test');
79+
}
80+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy