Skip to content

Commit fcbfc45

Browse files
committed
Validate the extended type for lazy-loaded type extensions
1 parent 2c386ad commit fcbfc45

File tree

4 files changed

+94
-1
lines changed

4 files changed

+94
-1
lines changed

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
-----

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

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)