Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit af4a624

Browse files
committed
feature #15743 Validate the extended type for lazy-loaded type extensions (stof)
This PR was merged into the 2.8 branch. Discussion ---------- Validate the extended type for lazy-loaded type extensions | Q | A | ------------- | --- | Bug fix? | no | New feature? | not really | BC breaks? | yes, but only for broken setups | Deprecations? | no | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | n/a Symfony 2.1 introduced such validation for form types because a mismatch would actually break the logic (the name is accessed again later). This was not added for type extensions because in such case, ``getExtendedType`` would actually never be used for extensions loaded by the DI extension (this method is only used inside extensions, and the DI extension relies on the service configuration instead). However, having mismatching values there would make debugging much harder, and can hide mistakes (see #15740 for such a mistake being fixed in the core). It also means that it might be hard to fix usage of deprecated APIs as the code might not contain the same extended type than the one used in the fullstack usage. Commits ------- 8826d39 Validate the extended type for lazy-loaded type extensions
2 parents 6393ec3 + 8826d39 commit af4a624

File tree

5 files changed

+139
-42
lines changed

5 files changed

+139
-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
@@ -68,7 +68,18 @@ public function getTypeExtensions($name)
6868

6969
if (isset($this->typeExtensionServiceIds[$name])) {
7070
foreach ($this->typeExtensionServiceIds[$name] as $serviceId) {
71-
$extensions[] = $this->container->get($serviceId);
71+
$extensions[] = $extension = $this->container->get($serviceId);
72+
73+
// validate result of getExtendedType() to ensure it is consistent with the service definition
74+
if ($extension->getExtendedType() !== $name) {
75+
throw new InvalidArgumentException(
76+
sprintf('The extended type specified for the service "%s" does not match the actual extended type. Expected "%s", given "%s".',
77+
$serviceId,
78+
$name,
79+
$extension->getExtendedType()
80+
)
81+
);
82+
}
7283
}
7384
}
7485

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 <[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\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+
}

src/Symfony/Component/Form/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"symfony/phpunit-bridge": "~2.7|~3.0.0",
2727
"doctrine/collections": "~1.0",
2828
"symfony/validator": "~2.8|~3.0.0",
29+
"symfony/dependency-injection": "~2.3|~3.0.0",
2930
"symfony/http-foundation": "~2.2|~3.0.0",
3031
"symfony/http-kernel": "~2.4|~3.0.0",
3132
"symfony/security-csrf": "~2.4|~3.0.0",

0 commit comments

Comments
 (0)