diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md
index 24861929c8ae..6a7282172db1 100644
--- a/UPGRADE-2.8.md
+++ b/UPGRADE-2.8.md
@@ -21,7 +21,7 @@ Form
```php
use Symfony\Component\Validator\Constraints\Valid;
-
+
$form = $this->createFormBuilder($article)
->add('author', new AuthorType(), array(
'constraints' => new Valid(),
@@ -42,42 +42,42 @@ Form
private $author;
}
```
-
+
* Type names were deprecated and will be removed in Symfony 3.0. Instead of
referencing types by name, you should reference them by their
fully-qualified class name (FQCN) instead. With PHP 5.5 or later, you can
use the "class" constant for that:
-
+
Before:
-
+
```php
$form = $this->createFormBuilder()
->add('name', 'text')
->add('age', 'integer')
->getForm();
```
-
+
After:
-
+
```php
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
-
+
$form = $this->createFormBuilder()
->add('name', TextType::class)
->add('age', IntegerType::class)
->getForm();
```
-
+
As a further consequence, the method `FormTypeInterface::getName()` was
deprecated and will be removed in Symfony 3.0. You should remove this method
from your form types.
-
+
If you want to customize the block prefix of a type in Twig, you should now
implement `FormTypeInterface::getBlockPrefix()` instead:
-
+
Before:
-
+
```php
class UserProfileType extends AbstractType
{
@@ -87,9 +87,9 @@ Form
}
}
```
-
+
After:
-
+
```php
class UserProfileType extends AbstractType
{
@@ -99,14 +99,14 @@ Form
}
}
```
-
+
If you don't customize `getBlockPrefix()`, it defaults to the class name
without "Type" suffix in underscore notation (here: "user_profile").
-
+
If you want to create types that are compatible with Symfony 2.3 up to 2.8
and don't trigger deprecation errors, implement *both* `getName()` and
`getBlockPrefix()`:
-
+
```php
class ProfileType extends AbstractType
{
@@ -114,38 +114,38 @@ Form
{
return $this->getBlockPrefix();
}
-
+
public function getBlockPrefix()
{
return 'profile';
}
}
```
-
+
If you define your form types in the Dependency Injection configuration, you
should further remove the "alias" attribute:
-
+
Before:
-
+
```xml
```
-
+
After:
-
+
```xml
```
-
+
Type extension should return the fully-qualified class name of the extended
type from `FormTypeExtensionInterface::getExtendedType()` now.
-
+
Before:
-
+
```php
class MyTypeExtension extends AbstractTypeExtension
{
@@ -155,12 +155,12 @@ Form
}
}
```
-
+
After:
-
+
```php
use Symfony\Component\Form\Extension\Core\Type\FormType;
-
+
class MyTypeExtension extends AbstractTypeExtension
{
public function getExtendedType()
@@ -169,14 +169,14 @@ Form
}
}
```
-
+
If your extension has to be compatible with Symfony 2.3-2.8, use the
following statement:
-
+
```php
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
-
+
class MyTypeExtension extends AbstractTypeExtension
{
public function getExtendedType()
@@ -185,13 +185,13 @@ Form
}
}
```
-
+
* Returning type instances from `FormTypeInterface::getParent()` is deprecated
and will not be supported anymore in Symfony 3.0. Return the fully-qualified
class name of the parent type class instead.
-
+
Before:
-
+
```php
class MyType
{
@@ -201,9 +201,9 @@ Form
}
}
```
-
+
After:
-
+
```php
class MyType
{
@@ -213,24 +213,28 @@ Form
}
}
```
-
+
* Passing type instances to `Form::add()`, `FormBuilder::add()` and the
`FormFactory::create*()` methods is deprecated and will not be supported
anymore in Symfony 3.0. Pass the fully-qualified class name of the type
instead.
-
+
Before:
-
+
```php
$form = $this->createForm(new MyType());
```
-
+
After:
-
+
```php
$form = $this->createForm(MyType::class);
```
+ * Registering type extensions as a service with an alias which does not
+ match the type returned by `getExtendedType` is now forbidden. Fix your
+ implementation to define the right type.
+
Translator
----------
diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md
index 76b2da1da319..cebd4b87ac47 100644
--- a/src/Symfony/Component/Form/CHANGELOG.md
+++ b/src/Symfony/Component/Form/CHANGELOG.md
@@ -9,6 +9,7 @@ CHANGELOG
* deprecated the "cascade_validation" option in favor of setting "constraints"
with the Valid constraint
* moved data trimming logic of TrimListener into StringUtil
+ * [BC BREAK] When registering a type extension through the DI extension, the tag alias has to match the actual extended type.
2.7.0
-----
diff --git a/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php b/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php
index 487a9aa8b75e..3a8a6b254523 100644
--- a/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php
+++ b/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php
@@ -68,7 +68,18 @@ public function getTypeExtensions($name)
if (isset($this->typeExtensionServiceIds[$name])) {
foreach ($this->typeExtensionServiceIds[$name] as $serviceId) {
- $extensions[] = $this->container->get($serviceId);
+ $extensions[] = $extension = $this->container->get($serviceId);
+
+ // validate result of getExtendedType() to ensure it is consistent with the service definition
+ if ($extension->getExtendedType() !== $name) {
+ throw new InvalidArgumentException(
+ sprintf('The extended type specified for the service "%s" does not match the actual extended type. Expected "%s", given "%s".',
+ $serviceId,
+ $name,
+ $extension->getExtendedType()
+ )
+ );
+ }
}
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php
new file mode 100644
index 000000000000..77fb370d73e0
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php
@@ -0,0 +1,80 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
+use Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension;
+
+class DependencyInjectionExtensionTest extends \PHPUnit_Framework_TestCase
+{
+ public function testGetTypeExtensions()
+ {
+ $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
+
+ $typeExtension1 = $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface');
+ $typeExtension1->expects($this->any())
+ ->method('getExtendedType')
+ ->willReturn('test');
+ $typeExtension2 = $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface');
+ $typeExtension2->expects($this->any())
+ ->method('getExtendedType')
+ ->willReturn('test');
+ $typeExtension3 = $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface');
+ $typeExtension3->expects($this->any())
+ ->method('getExtendedType')
+ ->willReturn('other');
+
+ $services = array(
+ 'extension1' => $typeExtension1,
+ 'extension2' => $typeExtension2,
+ 'extension3' => $typeExtension3,
+ );
+
+ $container->expects($this->any())
+ ->method('get')
+ ->willReturnCallback(function ($id) use ($services) {
+ if (isset($services[$id])) {
+ return $services[$id];
+ }
+
+ throw new ServiceNotFoundException($id);
+ });
+
+ $extension = new DependencyInjectionExtension($container, array(), array('test' => array('extension1', 'extension2'), 'other' => array('extension3')), array());
+
+ $this->assertTrue($extension->hasTypeExtensions('test'));
+ $this->assertFalse($extension->hasTypeExtensions('unknown'));
+ $this->assertSame(array($typeExtension1, $typeExtension2), $extension->getTypeExtensions('test'));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException
+ */
+ public function testThrowExceptionForInvalidExtendedType()
+ {
+ $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
+
+ $typeExtension = $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface');
+ $typeExtension->expects($this->any())
+ ->method('getExtendedType')
+ ->willReturn('unmatched');
+
+ $container->expects($this->any())
+ ->method('get')
+ ->with('extension')
+ ->willReturn($typeExtension);
+
+ $extension = new DependencyInjectionExtension($container, array(), array('test' => array('extension')), array());
+
+ $extension->getTypeExtensions('test');
+ }
+}
diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json
index 66a8674ab70f..b777792e1f32 100644
--- a/src/Symfony/Component/Form/composer.json
+++ b/src/Symfony/Component/Form/composer.json
@@ -26,6 +26,7 @@
"symfony/phpunit-bridge": "~2.7|~3.0.0",
"doctrine/collections": "~1.0",
"symfony/validator": "~2.8|~3.0.0",
+ "symfony/dependency-injection": "~2.3|~3.0.0",
"symfony/http-foundation": "~2.2|~3.0.0",
"symfony/http-kernel": "~2.4|~3.0.0",
"symfony/security-csrf": "~2.4|~3.0.0",