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

Skip to content

Commit b118226

Browse files
committed
bug #22481 [FrameworkBundle] Restore 3.2-like behavior for FormPass, to help BC with Sonata (nicolas-grekas)
This PR was merged into the 3.3-dev branch. Discussion ---------- [FrameworkBundle] Restore 3.2-like behavior for FormPass, to help BC with Sonata | Q | A | ------------- | --- | Branch? | 3.3 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - I tried updating a Sonata project to 3.3, and found it broken. The issue is that Sonata uses the constructor arguments of the `form.extension` to create its own `form.extension` service - but borrows its first args from the Symfony one. Here is the form pass doing that: https://github.com/sonata-project/SonataCoreBundle/blob/3.x/DependencyInjection/Compiler/FormFactoryCompilerPass.php And the implementation of the form extension: https://github.com/sonata-project/SonataCoreBundle/blob/3.x/DependencyInjection/SonataCoreExtension.php Question: is this covered by the BC policy? It shouldn't to me, because that would prevent *any* service reconfiguration. Thus, I'm proposing the attached patch, which basically reverts the deprecated `FormPass` in FrameworkBundle to its 3.2 state. I added a check to the new `FormPass` in the Form component so that it doesn't overwrite such compatibility configurations. See for corresponding fix on sonata-project/SonataCoreBundle#399 Commits ------- c97b08e [FrameworkBundle] Restore 3.2-like behavior for FormPass, to help BC with Sonata
2 parents 0257013 + c97b08e commit b118226

File tree

4 files changed

+141
-103
lines changed

4 files changed

+141
-103
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,76 @@
1313

1414
@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use Symfony\Component\Form\DependencyInjection\FormPass instead.', FormPass::class), E_USER_DEPRECATED);
1515

16-
use Symfony\Component\Form\DependencyInjection\FormPass as BaseFormPass;
16+
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
17+
use Symfony\Component\DependencyInjection\ContainerBuilder;
18+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
19+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1720

1821
/**
1922
* Adds all services with the tags "form.type" and "form.type_guesser" as
2023
* arguments of the "form.extension" service.
2124
*
22-
* @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseFormPass} instead.
23-
*
2425
* @author Bernhard Schussek <[email protected]>
26+
*
27+
* @deprecated since version 3.3, to be removed in 4.0. Use FormPass in the Form component instead.
2528
*/
26-
class FormPass extends BaseFormPass
29+
class FormPass implements CompilerPassInterface
2730
{
31+
use PriorityTaggedServiceTrait;
32+
33+
public function process(ContainerBuilder $container)
34+
{
35+
if (!$container->hasDefinition('form.extension')) {
36+
return;
37+
}
38+
39+
$definition = $container->getDefinition('form.extension');
40+
41+
// Builds an array with fully-qualified type class names as keys and service IDs as values
42+
$types = array();
43+
44+
foreach ($container->findTaggedServiceIds('form.type') as $serviceId => $tag) {
45+
$serviceDefinition = $container->getDefinition($serviceId);
46+
if (!$serviceDefinition->isPublic()) {
47+
$serviceDefinition->setPublic(true);
48+
}
49+
50+
// Support type access by FQCN
51+
$types[$serviceDefinition->getClass()] = $serviceId;
52+
}
53+
54+
$definition->replaceArgument(1, $types);
55+
56+
$typeExtensions = array();
57+
58+
foreach ($this->findAndSortTaggedServices('form.type_extension', $container) as $reference) {
59+
$serviceId = (string) $reference;
60+
$serviceDefinition = $container->getDefinition($serviceId);
61+
if (!$serviceDefinition->isPublic()) {
62+
$serviceDefinition->setPublic(true);
63+
}
64+
65+
$tag = $serviceDefinition->getTag('form.type_extension');
66+
if (isset($tag[0]['extended_type'])) {
67+
$extendedType = $tag[0]['extended_type'];
68+
} else {
69+
throw new InvalidArgumentException(sprintf('Tagged form type extension must have the extended type configured using the extended_type/extended-type attribute, none was configured for the "%s" service.', $serviceId));
70+
}
71+
72+
$typeExtensions[$extendedType][] = $serviceId;
73+
}
74+
75+
$definition->replaceArgument(2, $typeExtensions);
76+
77+
// Find all services annotated with "form.type_guesser"
78+
$guessers = array_keys($container->findTaggedServiceIds('form.type_guesser'));
79+
foreach ($guessers as $serviceId) {
80+
$serviceDefinition = $container->getDefinition($serviceId);
81+
if (!$serviceDefinition->isPublic()) {
82+
$serviceDefinition->setPublic(true);
83+
}
84+
}
85+
86+
$definition->replaceArgument(3, $guessers);
87+
}
2888
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
<argument /><!-- All services with tag "form.type" are stored in a service locator by FormPass -->
3737
<argument type="collection" /><!-- All services with tag "form.type_extension" are stored here by FormPass -->
3838
<argument type="iterator" /><!-- All services with tag "form.type_guesser" are stored here by FormPass -->
39+
<argument>null</argument><!-- @deprecated argument in 3.3, to be removed in 4.0 -->
3940
</service>
4041

4142
<!-- ValidatorTypeGuesser -->

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

Lines changed: 73 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,9 @@
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\ServiceClosureArgument;
1816
use Symfony\Component\DependencyInjection\ContainerBuilder;
1917
use Symfony\Component\DependencyInjection\Definition;
2018
use Symfony\Component\DependencyInjection\Reference;
21-
use Symfony\Component\DependencyInjection\ServiceLocator;
2219
use Symfony\Component\Form\AbstractType;
2320

2421
/**
@@ -30,7 +27,8 @@ class FormPassTest extends TestCase
3027
{
3128
public function testDoNothingIfFormExtensionNotLoaded()
3229
{
33-
$container = $this->createContainerBuilder();
30+
$container = new ContainerBuilder();
31+
$container->addCompilerPass(new FormPass());
3432

3533
$container->compile();
3634

@@ -39,33 +37,47 @@ public function testDoNothingIfFormExtensionNotLoaded()
3937

4038
public function testAddTaggedTypes()
4139
{
42-
$container = $this->createContainerBuilder();
40+
$container = new ContainerBuilder();
41+
$container->addCompilerPass(new FormPass());
4342

44-
$container->setDefinition('form.extension', $this->createExtensionDefinition());
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+
));
50+
51+
$container->setDefinition('form.extension', $extDefinition);
4552
$container->register('my.type1', __CLASS__.'_Type1')->addTag('form.type');
4653
$container->register('my.type2', __CLASS__.'_Type2')->addTag('form.type');
4754

4855
$container->compile();
4956

5057
$extDefinition = $container->getDefinition('form.extension');
5158

52-
$this->assertEquals(
53-
(new Definition(ServiceLocator::class, array(array(
54-
__CLASS__.'_Type1' => new ServiceClosureArgument(new Reference('my.type1')),
55-
__CLASS__.'_Type2' => new ServiceClosureArgument(new Reference('my.type2')),
56-
))))->addTag('container.service_locator')->setPublic(false),
57-
$extDefinition->getArgument(0)
58-
);
59+
$this->assertEquals(array(
60+
__CLASS__.'_Type1' => 'my.type1',
61+
__CLASS__.'_Type2' => 'my.type2',
62+
), $extDefinition->getArgument(1));
5963
}
6064

6165
/**
6266
* @dataProvider addTaggedTypeExtensionsDataProvider
6367
*/
6468
public function testAddTaggedTypeExtensions(array $extensions, array $expectedRegisteredExtensions)
6569
{
66-
$container = $this->createContainerBuilder();
70+
$container = new ContainerBuilder();
71+
$container->addCompilerPass(new FormPass());
6772

68-
$container->setDefinition('form.extension', $this->createExtensionDefinition());
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);
6981

7082
foreach ($extensions as $serviceId => $tag) {
7183
$container->register($serviceId, 'stdClass')->addTag('form.type_extension', $tag);
@@ -74,7 +86,7 @@ public function testAddTaggedTypeExtensions(array $extensions, array $expectedRe
7486
$container->compile();
7587

7688
$extDefinition = $container->getDefinition('form.extension');
77-
$this->assertEquals($expectedRegisteredExtensions, $extDefinition->getArgument(1));
89+
$this->assertSame($expectedRegisteredExtensions, $extDefinition->getArgument(2));
7890
}
7991

8092
/**
@@ -90,11 +102,8 @@ public function addTaggedTypeExtensionsDataProvider()
90102
'my.type_extension3' => array('extended_type' => 'type2'),
91103
),
92104
array(
93-
'type1' => new IteratorArgument(array(
94-
new Reference('my.type_extension1'),
95-
new Reference('my.type_extension2'),
96-
)),
97-
'type2' => new IteratorArgument(array(new Reference('my.type_extension3'))),
105+
'type1' => array('my.type_extension1', 'my.type_extension2'),
106+
'type2' => array('my.type_extension3'),
98107
),
99108
),
100109
array(
@@ -107,16 +116,8 @@ public function addTaggedTypeExtensionsDataProvider()
107116
'my.type_extension6' => array('extended_type' => 'type2', 'priority' => 1),
108117
),
109118
array(
110-
'type1' => new IteratorArgument(array(
111-
new Reference('my.type_extension2'),
112-
new Reference('my.type_extension1'),
113-
new Reference('my.type_extension3'),
114-
)),
115-
'type2' => new IteratorArgument(array(
116-
new Reference('my.type_extension4'),
117-
new Reference('my.type_extension5'),
118-
new Reference('my.type_extension6'),
119-
)),
119+
'type1' => array('my.type_extension2', 'my.type_extension1', 'my.type_extension3'),
120+
'type2' => array('my.type_extension4', 'my.type_extension5', 'my.type_extension6'),
120121
),
121122
),
122123
);
@@ -128,9 +129,17 @@ public function addTaggedTypeExtensionsDataProvider()
128129
*/
129130
public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttribute()
130131
{
131-
$container = $this->createContainerBuilder();
132+
$container = new ContainerBuilder();
133+
$container->addCompilerPass(new FormPass());
134+
135+
$extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array(
136+
new Reference('service_container'),
137+
array(),
138+
array(),
139+
array(),
140+
));
132141

133-
$container->setDefinition('form.extension', $this->createExtensionDefinition());
142+
$container->setDefinition('form.extension', $extDefinition);
134143
$container->register('my.type_extension', 'stdClass')
135144
->addTag('form.type_extension');
136145

@@ -139,102 +148,67 @@ public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttribute()
139148

140149
public function testAddTaggedGuessers()
141150
{
142-
$container = $this->createContainerBuilder();
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+
));
143161

144162
$definition1 = new Definition('stdClass');
145163
$definition1->addTag('form.type_guesser');
146164
$definition2 = new Definition('stdClass');
147165
$definition2->addTag('form.type_guesser');
148166

149-
$container->setDefinition('form.extension', $this->createExtensionDefinition());
167+
$container->setDefinition('form.extension', $extDefinition);
150168
$container->setDefinition('my.guesser1', $definition1);
151169
$container->setDefinition('my.guesser2', $definition2);
152170

153171
$container->compile();
154172

155173
$extDefinition = $container->getDefinition('form.extension');
156174

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

166181
/**
167182
* @dataProvider privateTaggedServicesProvider
168183
*/
169-
public function testPrivateTaggedServices($id, $tagName, callable $assertion, array $tagAttributes = array())
184+
public function testPrivateTaggedServices($id, $tagName)
170185
{
171-
$formPass = new FormPass();
172186
$container = new ContainerBuilder();
187+
$container->addCompilerPass(new FormPass());
173188

174-
$container->setDefinition('form.extension', $this->createExtensionDefinition());
175-
$container->register($id, 'stdClass')->setPublic(false)->addTag($tagName, $tagAttributes);
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+
));
176196

177-
$formPass->process($container);
197+
$container->setDefinition('form.extension', $extDefinition);
198+
$container->register($id, 'stdClass')->setPublic(false)->addTag($tagName, array('extended_type' => 'Foo'));
178199

179-
$assertion($container);
200+
$container->compile();
201+
$this->assertTrue($container->getDefinition($id)->isPublic());
180202
}
181203

182204
public function privateTaggedServicesProvider()
183205
{
184206
return array(
185-
array(
186-
'my.type',
187-
'form.type',
188-
function (ContainerBuilder $container) {
189-
$formTypes = $container->getDefinition('form.extension')->getArgument(0);
190-
191-
$this->assertInstanceOf(Reference::class, $formTypes);
192-
193-
$locator = $container->getDefinition((string) $formTypes);
194-
$expectedLocatorMap = array(
195-
'stdClass' => new ServiceClosureArgument(new Reference('my.type')),
196-
);
197-
198-
$this->assertInstanceOf(Definition::class, $locator);
199-
$this->assertEquals($expectedLocatorMap, $locator->getArgument(0));
200-
},
201-
),
202-
array(
203-
'my.type_extension',
204-
'form.type_extension',
205-
function (ContainerBuilder $container) {
206-
$this->assertEquals(
207-
array('Symfony\Component\Form\Extension\Core\Type\FormType' => new IteratorArgument(array(new Reference('my.type_extension')))),
208-
$container->getDefinition('form.extension')->getArgument(1)
209-
);
210-
},
211-
array('extended_type' => 'Symfony\Component\Form\Extension\Core\Type\FormType'),
212-
),
213-
array('my.guesser', 'form.type_guesser', function (ContainerBuilder $container) {
214-
$this->assertEquals(new IteratorArgument(array(new Reference('my.guesser'))), $container->getDefinition('form.extension')->getArgument(2));
215-
}),
207+
array('my.type', 'form.type'),
208+
array('my.type_extension', 'form.type_extension'),
209+
array('my.guesser', 'form.type_guesser'),
216210
);
217211
}
218-
219-
private function createContainerBuilder()
220-
{
221-
$container = new ContainerBuilder();
222-
$container->addCompilerPass(new FormPass());
223-
224-
return $container;
225-
}
226-
227-
private function createExtensionDefinition()
228-
{
229-
$definition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension');
230-
$definition->setArguments(array(
231-
array(),
232-
array(),
233-
new IteratorArgument(array()),
234-
));
235-
236-
return $definition;
237-
}
238212
}
239213

240214
class FormPassTest_Type1 extends AbstractType

src/Symfony/Component/Form/DependencyInjection/FormPass.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ public function process(ContainerBuilder $container)
5050
}
5151

5252
$definition = $container->getDefinition($this->formExtensionService);
53+
if (new IteratorArgument(array()) != $definition->getArgument(2)) {
54+
return;
55+
}
5356
$definition->replaceArgument(0, $this->processFormTypes($container, $definition));
5457
$definition->replaceArgument(1, $this->processFormTypeExtensions($container));
5558
$definition->replaceArgument(2, $this->processFormTypeGuessers($container));

0 commit comments

Comments
 (0)