Skip to content

Commit a79ae95

Browse files
committed
[FrameworkBundle][Form] Move FormPass to the Form component
1 parent 4831d3c commit a79ae95

File tree

4 files changed

+320
-1
lines changed

4 files changed

+320
-1
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
3.3.0
5+
-----
6+
7+
* added `FormPass`
8+
49
3.2.0
510
-----
611

DependencyInjection/FormPass.php

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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\DependencyInjection;
13+
14+
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
17+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
18+
19+
/**
20+
* Adds all services with the tags "form.type" and "form.type_guesser" as
21+
* arguments of the "form.extension" service.
22+
*
23+
* @author Bernhard Schussek <bschussek@gmail.com>
24+
*/
25+
class FormPass implements CompilerPassInterface
26+
{
27+
use PriorityTaggedServiceTrait;
28+
29+
private $formExtensionService;
30+
private $formTypeTag;
31+
private $formTypeExtensionTag;
32+
private $formTypeGuesserTag;
33+
34+
public function __construct($formExtensionService = 'form.extension', $formTypeTag = 'form.type', $formTypeExtensionTag = 'form.type_extension', $formTypeGuesserTag = 'form.type_guesser')
35+
{
36+
$this->formExtensionService = $formExtensionService;
37+
$this->formTypeTag = $formTypeTag;
38+
$this->formTypeExtensionTag = $formTypeExtensionTag;
39+
$this->formTypeGuesserTag = $formTypeGuesserTag;
40+
}
41+
42+
public function process(ContainerBuilder $container)
43+
{
44+
if (!$container->hasDefinition($this->formExtensionService)) {
45+
return;
46+
}
47+
48+
$definition = $container->getDefinition($this->formExtensionService);
49+
50+
// Builds an array with fully-qualified type class names as keys and service IDs as values
51+
$types = array();
52+
foreach ($container->findTaggedServiceIds($this->formTypeTag) as $serviceId => $tag) {
53+
$serviceDefinition = $container->getDefinition($serviceId);
54+
if (!$serviceDefinition->isPublic()) {
55+
throw new InvalidArgumentException(sprintf('The service "%s" must be public as form types are lazy-loaded.', $serviceId));
56+
}
57+
58+
// Support type access by FQCN
59+
$types[$serviceDefinition->getClass()] = $serviceId;
60+
}
61+
62+
$definition->replaceArgument(1, $types);
63+
64+
$typeExtensions = array();
65+
66+
foreach ($this->findAndSortTaggedServices($this->formTypeExtensionTag, $container) as $reference) {
67+
$serviceId = (string) $reference;
68+
$serviceDefinition = $container->getDefinition($serviceId);
69+
if (!$serviceDefinition->isPublic()) {
70+
throw new InvalidArgumentException(sprintf('The service "%s" must be public as form type extensions are lazy-loaded.', $serviceId));
71+
}
72+
73+
$tag = $serviceDefinition->getTag($this->formTypeExtensionTag);
74+
if (isset($tag[0]['extended_type'])) {
75+
$extendedType = $tag[0]['extended_type'];
76+
} else {
77+
throw new InvalidArgumentException(sprintf('"%s" tagged services must have the extended type configured using the extended_type/extended-type attribute, none was configured for the "%s" service.', $this->formTypeExtensionTag, $serviceId));
78+
}
79+
80+
$typeExtensions[$extendedType][] = $serviceId;
81+
}
82+
83+
$definition->replaceArgument(2, $typeExtensions);
84+
85+
$guessers = array_keys($container->findTaggedServiceIds($this->formTypeGuesserTag));
86+
foreach ($guessers as $serviceId) {
87+
$serviceDefinition = $container->getDefinition($serviceId);
88+
if (!$serviceDefinition->isPublic()) {
89+
throw new InvalidArgumentException(sprintf('The service "%s" must be public as form type guessers are lazy-loaded.', $serviceId));
90+
}
91+
}
92+
93+
$definition->replaceArgument(3, $guessers);
94+
}
95+
}
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
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\DependencyInjection;
13+
14+
use Symfony\Component\Form\DependencyInjection\FormPass;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\Definition;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
use Symfony\Component\Form\AbstractType;
19+
20+
/**
21+
* @author Bernhard Schussek <bschussek@gmail.com>
22+
*/
23+
class FormPassTest extends \PHPUnit_Framework_TestCase
24+
{
25+
public function testDoNothingIfFormExtensionNotLoaded()
26+
{
27+
$container = new ContainerBuilder();
28+
$container->addCompilerPass(new FormPass());
29+
30+
$container->compile();
31+
32+
$this->assertFalse($container->hasDefinition('form.extension'));
33+
}
34+
35+
public function testAddTaggedTypes()
36+
{
37+
$container = new ContainerBuilder();
38+
$container->addCompilerPass(new FormPass());
39+
40+
$extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension');
41+
$extDefinition->setArguments(array(
42+
new Reference('service_container'),
43+
array(),
44+
array(),
45+
array(),
46+
));
47+
48+
$container->setDefinition('form.extension', $extDefinition);
49+
$container->register('my.type1', __CLASS__.'_Type1')->addTag('form.type');
50+
$container->register('my.type2', __CLASS__.'_Type2')->addTag('form.type');
51+
52+
$container->compile();
53+
54+
$extDefinition = $container->getDefinition('form.extension');
55+
56+
$this->assertEquals(array(
57+
__CLASS__.'_Type1' => 'my.type1',
58+
__CLASS__.'_Type2' => 'my.type2',
59+
), $extDefinition->getArgument(1));
60+
}
61+
62+
/**
63+
* @dataProvider addTaggedTypeExtensionsDataProvider
64+
*/
65+
public function testAddTaggedTypeExtensions(array $extensions, array $expectedRegisteredExtensions)
66+
{
67+
$container = new ContainerBuilder();
68+
$container->addCompilerPass(new FormPass());
69+
70+
$extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array(
71+
new Reference('service_container'),
72+
array(),
73+
array(),
74+
array(),
75+
));
76+
77+
$container->setDefinition('form.extension', $extDefinition);
78+
79+
foreach ($extensions as $serviceId => $tag) {
80+
$container->register($serviceId, 'stdClass')->addTag('form.type_extension', $tag);
81+
}
82+
83+
$container->compile();
84+
85+
$extDefinition = $container->getDefinition('form.extension');
86+
$this->assertSame($expectedRegisteredExtensions, $extDefinition->getArgument(2));
87+
}
88+
89+
/**
90+
* @return array
91+
*/
92+
public function addTaggedTypeExtensionsDataProvider()
93+
{
94+
return array(
95+
array(
96+
array(
97+
'my.type_extension1' => array('extended_type' => 'type1'),
98+
'my.type_extension2' => array('extended_type' => 'type1'),
99+
'my.type_extension3' => array('extended_type' => 'type2'),
100+
),
101+
array(
102+
'type1' => array('my.type_extension1', 'my.type_extension2'),
103+
'type2' => array('my.type_extension3'),
104+
),
105+
),
106+
array(
107+
array(
108+
'my.type_extension1' => array('extended_type' => 'type1', 'priority' => 1),
109+
'my.type_extension2' => array('extended_type' => 'type1', 'priority' => 2),
110+
'my.type_extension3' => array('extended_type' => 'type1', 'priority' => -1),
111+
'my.type_extension4' => array('extended_type' => 'type2', 'priority' => 2),
112+
'my.type_extension5' => array('extended_type' => 'type2', 'priority' => 1),
113+
'my.type_extension6' => array('extended_type' => 'type2', 'priority' => 1),
114+
),
115+
array(
116+
'type1' => array('my.type_extension2', 'my.type_extension1', 'my.type_extension3'),
117+
'type2' => array('my.type_extension4', 'my.type_extension5', 'my.type_extension6'),
118+
),
119+
),
120+
);
121+
}
122+
123+
/**
124+
* @expectedException \InvalidArgumentException
125+
* @expectedExceptionMessage extended-type attribute, none was configured for the "my.type_extension" service
126+
*/
127+
public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttribute()
128+
{
129+
$container = new ContainerBuilder();
130+
$container->addCompilerPass(new FormPass());
131+
132+
$extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array(
133+
new Reference('service_container'),
134+
array(),
135+
array(),
136+
array(),
137+
));
138+
139+
$container->setDefinition('form.extension', $extDefinition);
140+
$container->register('my.type_extension', 'stdClass')
141+
->addTag('form.type_extension');
142+
143+
$container->compile();
144+
}
145+
146+
public function testAddTaggedGuessers()
147+
{
148+
$container = new ContainerBuilder();
149+
$container->addCompilerPass(new FormPass());
150+
151+
$extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension');
152+
$extDefinition->setArguments(array(
153+
new Reference('service_container'),
154+
array(),
155+
array(),
156+
array(),
157+
));
158+
159+
$definition1 = new Definition('stdClass');
160+
$definition1->addTag('form.type_guesser');
161+
$definition2 = new Definition('stdClass');
162+
$definition2->addTag('form.type_guesser');
163+
164+
$container->setDefinition('form.extension', $extDefinition);
165+
$container->setDefinition('my.guesser1', $definition1);
166+
$container->setDefinition('my.guesser2', $definition2);
167+
168+
$container->compile();
169+
170+
$extDefinition = $container->getDefinition('form.extension');
171+
172+
$this->assertSame(array(
173+
'my.guesser1',
174+
'my.guesser2',
175+
), $extDefinition->getArgument(3));
176+
}
177+
178+
/**
179+
* @dataProvider privateTaggedServicesProvider
180+
*/
181+
public function testPrivateTaggedServices($id, $tagName, $expectedExceptionMessage)
182+
{
183+
$container = new ContainerBuilder();
184+
$container->addCompilerPass(new FormPass());
185+
186+
$extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension');
187+
$extDefinition->setArguments(array(
188+
new Reference('service_container'),
189+
array(),
190+
array(),
191+
array(),
192+
));
193+
194+
$container->setDefinition('form.extension', $extDefinition);
195+
$container->register($id, 'stdClass')->setPublic(false)->addTag($tagName);
196+
197+
$this->setExpectedException('\InvalidArgumentException', $expectedExceptionMessage);
198+
199+
$container->compile();
200+
}
201+
202+
public function privateTaggedServicesProvider()
203+
{
204+
return array(
205+
array('my.type', 'form.type', 'The service "my.type" must be public as form types are lazy-loaded'),
206+
array('my.type_extension', 'form.type_extension', 'The service "my.type_extension" must be public as form type extensions are lazy-loaded'),
207+
array('my.guesser', 'form.type_guesser', 'The service "my.guesser" must be public as form type guessers are lazy-loaded'),
208+
);
209+
}
210+
}
211+
212+
class FormPassTest_Type1 extends AbstractType
213+
{
214+
}
215+
216+
class FormPassTest_Type2 extends AbstractType
217+
{
218+
}

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"require-dev": {
2727
"doctrine/collections": "~1.0",
2828
"symfony/validator": "~2.8|~3.0",
29-
"symfony/dependency-injection": "~2.8|~3.0",
29+
"symfony/dependency-injection": "~3.2",
30+
"symfony/config": "~2.7|~3.0",
3031
"symfony/http-foundation": "~2.8|~3.0",
3132
"symfony/http-kernel": "~2.8|~3.0",
3233
"symfony/security-csrf": "~2.8|~3.0",

0 commit comments

Comments
 (0)