8000 feature #21690 [Form] allow form types + form type extensions + form … · symfony/symfony@d1da474 · GitHub
[go: up one dir, main page]

Skip to content

Commit d1da474

Browse files
committed
feature #21690 [Form] allow form types + form type extensions + form type guessers to be private services (hhamon)
This PR was merged into the 3.3-dev branch. Discussion ---------- [Form] allow form types + form type extensions + form type guessers to be private services | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | ~ | License | MIT | Doc PR | ~ This pull request is about making internal form services (aka form types, form type extensions and form type guessers) private. They used to be public until Symfony 3.2 for one valid reason: lazyness. However, Symfony 3.3 now comes with built-in mechanism to support effective lazy loading of private services with service locators and proxies. This PR makes the `DependencyInjectionExtension` class of the `Form` component leverage these new DI component mechanisms. Form types, form type extensions and form type guessers can now be declared private as a best practice. We decided to make these services private as of Symfony 3.3 and of course it would break BC. But this PR introduces a BC layer using a Symfony trick to keep internal form services public. The service container currently has a known issue where private services are not really private if they're referenced by at least two other services in the container. We use this trick to maintain the legacy services public even though the new API relies on private ones. This trick is done thanks to the `deprecated.form.registry` and `deprecated.form.registry.csrf` fake services that will be removed in Symfony 4.0. Commits ------- 600e75c [Form] use new service locator in DependencyInjectionExtension class, so that form types can be made private at some point.
2 parents 59a3b51 + 600e75c commit d1da474

File tree

9 files changed

+337
-250
lines changed

9 files changed

+337
-250
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,13 @@
3030

3131
<!-- DependencyInjectionExtension -->
3232
<service id="form.extension" class="Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension" public="false">
33-
<argument type="service" id="service_container" />
34-
<!-- All services with tag "form.type" are inserted here by FormPass -->
35-
<argument type="collection" />
36-
<!-- All services with tag "form.type_extension" are inserted here by FormPass -->
37-
<argument type="collection" />
38-
<!-- All services with tag "form.type_guesser" are inserted here by FormPass -->
39-
<argument type="collection" />
33+
<argument type="service-locator" /><!-- All services with tag "form.type" are stored in a service locator by FormPass -->
34+
<argument type="collection" /><!-- All services with tag "form.type_extension" are stored here by FormPass -->
35+
<argument type="iterator" /><!-- All services with tag "form.type_guesser" are stored here by FormPass -->
4036
</service>
4137

4238
<!-- ValidatorTypeGuesser -->
43-
<service id="form.type_guesser.validator" class="Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser">
39+
<service id="form.type_guesser.validator" class="Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser" public="false">
4440
<tag name="form.type_guesser" />
4541
<argument type="service" id="validator.mapping.class_metadata_factory" />
4642
</service>
@@ -61,7 +57,7 @@
6157

6258
<service id="form.choice_list_factory" alias="form.choice_list_factory.cached" public="false"/>
6359

64-
<service id="form.type.form" class="Symfony\Component\Form\Extension\Core\Type\FormType">
60+
<service id="form.type.form" class="Symfony\Component\Form\Extension\Core\Type\FormType" public="false">
6561
<argument type="service" id="form.property_accessor" />
6662
<tag name="form.type" />
6763
</service>
@@ -71,7 +67,7 @@
7167
<service id="form.type.checkbox" class="Symfony\Component\Form\Extension\Core\Type\CheckboxType">
7268
<deprecated>The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0.</deprecated>
7369
</service>
74-
<service id="form.type.choice" class="Symfony\Component\Form\Extension\Core\Type\ChoiceType">
70+
<service id="form.type.choice" class="Symfony\Component\Form\Extension\Core\Type\ChoiceType" public="false">
7571
<tag name="form.type" />
7672
<argument type="service" id="form.choice_list_factory"/>
7773
</service>
@@ -158,7 +154,7 @@
158154
</service>
159155

160156
<!-- FormTypeHttpFoundationExtension -->
161-
<service id="form.type_extension.form.http_foundation" class="Symfony\Component\Form\Extension\HttpFoundation\Type\FormTypeHttpFoundationExtension">
157+
<service id="form.type_extension.form.http_foundation" class="Symfony\Component\Form\Extension\HttpFoundation\Type\FormTypeHttpFoundationExtension" public="false">
162158
<argument type="service" id="form.type_extension.form.request_handler" />
163159
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FormType" />
164160
</service>
@@ -173,20 +169,34 @@
173169
</service>
174170

175171
<!-- FormTypeValidatorExtension -->
176-
<service id="form.type_extension.form.validator" class="Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension">
172+
<service id="form.type_extension.form.validator" class="Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension" public="false">
177173
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FormType" />
178174
<argument type="service" id="validator" />
179175
</service>
180-
<service id="form.type_extension.repeated.validator" class="Symfony\Component\Form\Extension\Validator\Type\RepeatedTypeValidatorExtension">
176+
<service id="form.type_extension.repeated.validator" class="Symfony\Component\Form\Extension\Validator\Type\RepeatedTypeValidatorExtension" public="false">
181177
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\RepeatedType" />
182178
</service>
183-
<service id="form.type_extension.submit.validator" class="Symfony\Component\Form\Extension\Validator\Type\SubmitTypeValidatorExtension">
179+
<service id="form.type_extension.submit.validator" class="Symfony\Component\Form\Extension\Validator\Type\SubmitTypeValidatorExtension" public="false">
184180
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\SubmitType" />
185181
</service>
186-
<service id="form.type_extension.upload.validator" class="Symfony\Component\Form\Extension\Validator\Type\UploadValidatorExtension">
182+
<service id="form.type_extension.upload.validator" class="Symfony\Component\Form\Extension\Validator\Type\UploadValidatorExtension" public="false">
187183
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FormType" />
188184
<argument type="service" id="translator"/>
189185
<argument type="string">%validator.translation_domain%</argument>
190186
</service>
187+
188+
<service id="deprecated.form.registry" class="stdClass">
189+
<property name="registry" type="collection">
190+
<property type="service" id="form.type_guesser.validator" />
191+
<property type="service" id="form.type.choice" />
192+
<property type="service" id="form.type.form" />
193+
<property type="service" id="form.type_extension.form.http_foundation" />
194+
<property type="service" id="form.type_extension.form.validator" />
195+
<property type="service" id="form.type_extension.repeated.validator" />
196+
<property type="service" id="form.type_extension.submit.validator" />
197+
<property type="service" id="form.type_extension.upload.validator" />
198+
</property>
199+
<deprecated>The service "%service_id%" is internal and deprecated since Symfony 3.3 and will be removed in Symfony 4.0</deprecated>
200+
</service>
191201
</services>
192202
</container>

src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml

Lines changed: 9 additions & 1 deletion
< 8000 tr>
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
66

77
<services>
8-
<service id="form.type_extension.csrf" class="Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension">
8+
<service id="form.type_extension.csrf" class="Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension" public="false">
99
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FormType" />
1010
<argument type="service" id="security.csrf.token_manager" />
1111
<argument>%form.type_extension.csrf.enabled%</argument>
@@ -14,5 +14,13 @@
1414
<argument>%validator.translation_domain%</argument>
1515
<argument type="service" id="form.server_params" />
1616
</service>
17+
18+
<service id="deprecated.form.registry.csrf" class="stdClass">
19+
<property name="registry" type="collection">
20+
<property type="service" id="form.type_extension.csrf" />
21+
</property>
22+
<deprecated>The service "%service_id%" is internal and deprecated since Symfony 3.3 and will be removed in Symfony 4.0</deprecated>
23+
</service>
24+
1725
</services>
1826
</container>

src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
</service>
1414

1515
<!-- DataCollectorTypeExtension -->
16-
<service id="form.type_extension.form.data_collector" class="Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension">
16+
<service id="form.type_extension.form.data_collector" class="Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension" public="false">
1717
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FormType" />
1818
<argument type="service" id="data_collector.form" />
1919
</service>

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php

Lines changed: 68 additions & 80 deletions
F438
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass;
16+
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
17+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1618
use Symfony\Component\DependencyInjection\ContainerBuilder;
1719
use Symfony\Component\DependencyInjection\Definition;
1820
use Symfony\Component\DependencyInjection\Reference;
@@ -27,8 +29,7 @@ class FormPassTest extends TestCase
2729
{
2830
public function testDoNothingIfFormExtensionNotLoaded()
2931
{
30-
$container = new ContainerBuilder();
31-
$container->addCompilerPass(new FormPass());
32+
$container = $this->createContainerBuilder();
3233

3334
$container->compile();
3435

@@ -37,47 +38,33 @@ public function testDoNothingIfFormExtensionNotLoaded()
3738

3839
public function testAddTaggedTypes()
3940
{
40-
$container = new ContainerBuilder();
41-
$container->addCompilerPass(new FormPass());
42-
43-
$extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension');
44-
$extDefinition->setArguments(array(
45-
new Reference('service_container'),
46-
array(),
47-
array(),
48-
array(),
49-
));
41+
$container = $this->createContainerBuilder();
5042

51-
$container->setDefinition('form.extension', $extDefinition);
43+
$container->setDefinition('form.extension', $this->createExtensionDefinition());
5244
$container->register('my.type1', __CLASS__.'_Type1')->addTag('form.type');
5345
$container->register('my.type2', __CLASS__.'_Type2')->addTag('form.type');
5446

5547
$container->compile();
5648

5749
$extDefinition = $container->getDefinition('form.extension');
5850

59-
$this->assertEquals(array(
60-
__CLASS__.'_Type1' => 'my.type1',
61-
__CLASS__.'_Type2' => 'my.type2',
62-
), $extDefinition->getArgument(1));
51+
$this->assertEquals(
52+
new ServiceLocatorArgument(array(
53+
__CLASS__.'_Type1' => new Reference('my.type1'),
54+
__CLASS__.'_Type2' => new Reference('my.type2'),
55+
)),
56+
$extDefinition->getArgument(0)
57+
);
6358
}
6459

6560
/**
6661
* @dataProvider addTaggedTypeExtensionsDataProvider
6762
*/
6863
public function testAddTaggedTypeExtensions(array $extensions, array $expectedRegisteredExtensions)
6964
{
70-
$container = new ContainerBuilder();
71-
$container->addCompilerPass(new FormPass());
65+
$container = $this->createContainerBuilder();
7266

73-
$extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array(
74-
new Reference('service_container'),
75-
array(),
76-
array(),
77-
array(),
78-
));
79-
80-
$container->setDefinition('form.extension', $extDefinition);
67+
$container->setDefinition('form.extension', $this->createExtensionDefinition());
8168

8269
foreach ($extensions as $serviceId => $tag) {
8370
$container->register($serviceId, 'stdClass')->addTag('form.type_extension', $tag);
@@ -86,7 +73,7 @@ public function testAddTaggedTypeExtensions(array $extensions, array $expectedRe
8673
$container->compile();
8774

8875
$extDefinition = $container->getDefinition('form.extension');
89-
$this->assertSame($expectedRegisteredExtensions, $extDefinition->getArgument(2));
76+
$this->assertEquals($expectedRegisteredExtensions, $extDefinition->getArgument(1));
9077
}
9178

9279
/**
@@ -102,8 +89,11 @@ public function addTaggedTypeExtensionsDataProvider()
10289
'my.type_extension3' => array('extended_type' => 'type2'),
10390
),
10491
array(
105-
'type1' => array('my.type_extension1', 'my.type_extension2'),
106-
'type2' => array('my.type_extension3'),
92+
'type1' => new IteratorArgument(array(
93+
new Reference('my.type_extension1'),
94+
new Reference('my.type_extension2'),
95+
)),
96+
'type2' => new IteratorArgument(array(new Reference('my.type_extension3'))),
10797
),
10898
),
10999
array(
@@ -116,8 +106,16 @@ public function addTaggedTypeExtensionsDataProvider()
116106
'my.type_extension6' => array('extended_type' => 'type2', 'priority' => 1),
117107
),
118108
array(
119-
'type1' => array('my.type_extension2', 'my.type_extension1', 'my.type_extension3'),
120-
'type2' => array('my.type_extension4', 'my.type_extension5', 'my.type_extension6'),
109+
'type1' => new IteratorArgument(array(
110+
new Reference('my.type_extension2'),
111+
new Reference('my.type_extension1'),
112+
new Reference('my.type_extension3'),
113+
)),
114+
'type2' => new IteratorArgument(array(
115+
new Reference('my.type_extension4'),
116+
new Reference('my.type_extension5'),
117+
new Reference('my.type_extension6'),
118+
)),
121119
),
122120
),
123121
);
@@ -129,17 +127,9 @@ public function addTaggedTypeExtensionsDataProvider()
129127
*/
130128
public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttribute()
131129
{
132-
$container = new ContainerBuilder();
133-
$container->addCompilerPass(new FormPass());
130+
$container = $this->createContainerBuilder();
134131

135-
$extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array(
136-
new Reference('service_container'),
137-
array(),
138-
array(),
139-
array(),
140-
));
141-
142-
$container->setDefinition('form.extension', $extDefinition);
132+
$container->setDefinition('form.extension', $this->createExtensionDefinition());
143133
$container->register('my.type_extension', 'stdClass')
144134
->addTag('form.type_extension');
145135

@@ -148,73 +138,71 @@ public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttribute()
148138

149139
public function testAddTaggedGuessers()
150140
{
151-
$container = new ContainerBuilder();
152-
$container->addCompilerPass(new FormPass());
153-
154-
$extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension');
155-
$extDefinition->setArguments(array(
156-
new Reference('service_container'),
157-
array(),
158-
array(),
159-
array(),
160-
));
141+
$container = $this->createContainerBuilder();
161142

162143
$definition1 = new Definition('stdClass');
163144
$definition1->addTag('form.type_guesser');
164145
$definition2 = new Definition('stdClass');
165146
$definition2->addTag('form.type_guesser');
166147

167-
$container->setDefinition('form.extension', $extDefinition);
148+
$container->setDefinition('form.extension', $this->createExtensionDefinition());
168149
$container->setDefinition('my.guesser1', $definition1);
169150
$container->setDefinition('my.guesser2', $definition2);
170151

171152
$container->compile();
172153

173154
$extDefinition = $container->getDefinition('form.extension');
174155

175-
$this->assertSame(array(
176-
'my.guesser1',
177-
'my.guesser2',
178-
), $extDefinition->getArgument(3));
156+
$this->assertEquals(
157+
new IteratorArgument(array(
158+
new Reference('my.guesser1'),
159+
new Reference('my.guesser2'),
160+
)),
161+
$extDefinition->getArgument(2)
162+
);
179163
}
180164

181165
/**
182166
* @dataProvider privateTaggedServicesProvider
183167
*/
184-
public function testPrivateTaggedServices($id, $tagName, $expectedExceptionMessage)
168+
public function testPrivateTaggedServices($id, $tagName, array $tagAttributes = array())
185169
{
186-
$container = new ContainerBuilder();
187-
$container->addCompilerPass(new FormPass());
188-
189-
$extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension');
190-
$extDefinition->setArguments(array(
191-
new Reference('service_container'),
192-
array(),
193-
array(),
194-
array(),
195-
));
196-
197-
$container->setDefinition('form.extension', $extDefinition);
198-
$container->register($id, 'stdClass')->setPublic(false)->addTag($tagName);
170+
$container = $this->createContainerBuilder();
199171

200-
if (method_exists($this, 'expectException')) {
201-
$this->expectException('InvalidArgumentException');
202-
$this->expectExceptionMessage($expectedExceptionMessage);
203-
} else {
204-
$this->setExpectedException('InvalidArgumentException', $expectedExceptionMessage);
205-
}
172+
$container->setDefinition('form.extension', $this->createExtensionDefinition());
173+
$container->register($id, 'stdClass')->setPublic(false)->addTag($tagName, $tagAttributes);
206174

207175
$container->compile();
208176
}
209177

210178
public function privateTaggedServicesProvider()
211179
{
212180
return array(
213-
array('my.type', 'form.type', 'The service "my.type" must be public as form types are lazy-loaded'),
214-
array('my.type_extension', 'form.type_extension', 'The service "my.type_extension" must be public as form type extensions are lazy-loaded'),
215-
array('my.guesser', 'form.type_guesser', 'The service "my.guesser" must be public as form type guessers are lazy-loaded'),
181+
array('my.type', 'form.type'),
182+
array('my.type_extension', 'form.type_extension', array('extended_type' => 'Symfony\Component\Form\Extension\Core\Type\FormType')),
183+
array('my.guesser', 'form.type_guesser'),
216184
);
217185
}
186+
187+
private function createContainerBuilder()
188+
{
189+
$container = new ContainerBuilder();
190+
$container->addCompilerPass(new FormPass());
191+
192+
return $container;
193+
}
194+
195+
private function createExtensionDefinition()
196+
{
197+
$definition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension');
198+
$definition->setArguments(array(
199+
new ServiceLocatorArgument(array()),
200+
array(),
201+
new IteratorArgument(array()),
202+
));
203+
204+
return $definition;
205+
}
218206
}
219207

220208
class FormPassTest_Type1 extends AbstractType

0 commit comments

Comments
 (0)
0