Validate the extended type for lazy-loaded type extensions
This commit is contained in:
parent
6393ec3169
commit
8826d39e0f
|
@ -21,7 +21,7 @@ Form
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Symfony\Component\Validator\Constraints\Valid;
|
use Symfony\Component\Validator\Constraints\Valid;
|
||||||
|
|
||||||
$form = $this->createFormBuilder($article)
|
$form = $this->createFormBuilder($article)
|
||||||
->add('author', new AuthorType(), array(
|
->add('author', new AuthorType(), array(
|
||||||
'constraints' => new Valid(),
|
'constraints' => new Valid(),
|
||||||
|
@ -42,42 +42,42 @@ Form
|
||||||
private $author;
|
private $author;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
* Type names were deprecated and will be removed in Symfony 3.0. Instead of
|
* Type names were deprecated and will be removed in Symfony 3.0. Instead of
|
||||||
referencing types by name, you should reference them by their
|
referencing types by name, you should reference them by their
|
||||||
fully-qualified class name (FQCN) instead. With PHP 5.5 or later, you can
|
fully-qualified class name (FQCN) instead. With PHP 5.5 or later, you can
|
||||||
use the "class" constant for that:
|
use the "class" constant for that:
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$form = $this->createFormBuilder()
|
$form = $this->createFormBuilder()
|
||||||
->add('name', 'text')
|
->add('name', 'text')
|
||||||
->add('age', 'integer')
|
->add('age', 'integer')
|
||||||
->getForm();
|
->getForm();
|
||||||
```
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
|
||||||
$form = $this->createFormBuilder()
|
$form = $this->createFormBuilder()
|
||||||
->add('name', TextType::class)
|
->add('name', TextType::class)
|
||||||
->add('age', IntegerType::class)
|
->add('age', IntegerType::class)
|
||||||
->getForm();
|
->getForm();
|
||||||
```
|
```
|
||||||
|
|
||||||
As a further consequence, the method `FormTypeInterface::getName()` was
|
As a further consequence, the method `FormTypeInterface::getName()` was
|
||||||
deprecated and will be removed in Symfony 3.0. You should remove this method
|
deprecated and will be removed in Symfony 3.0. You should remove this method
|
||||||
from your form types.
|
from your form types.
|
||||||
|
|
||||||
If you want to customize the block prefix of a type in Twig, you should now
|
If you want to customize the block prefix of a type in Twig, you should now
|
||||||
implement `FormTypeInterface::getBlockPrefix()` instead:
|
implement `FormTypeInterface::getBlockPrefix()` instead:
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class UserProfileType extends AbstractType
|
class UserProfileType extends AbstractType
|
||||||
{
|
{
|
||||||
|
@ -87,9 +87,9 @@ Form
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class UserProfileType extends AbstractType
|
class UserProfileType extends AbstractType
|
||||||
{
|
{
|
||||||
|
@ -99,14 +99,14 @@ Form
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If you don't customize `getBlockPrefix()`, it defaults to the class name
|
If you don't customize `getBlockPrefix()`, it defaults to the class name
|
||||||
without "Type" suffix in underscore notation (here: "user_profile").
|
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
|
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
|
and don't trigger deprecation errors, implement *both* `getName()` and
|
||||||
`getBlockPrefix()`:
|
`getBlockPrefix()`:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class ProfileType extends AbstractType
|
class ProfileType extends AbstractType
|
||||||
{
|
{
|
||||||
|
@ -114,38 +114,38 @@ Form
|
||||||
{
|
{
|
||||||
return $this->getBlockPrefix();
|
return $this->getBlockPrefix();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBlockPrefix()
|
public function getBlockPrefix()
|
||||||
{
|
{
|
||||||
return 'profile';
|
return 'profile';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If you define your form types in the Dependency Injection configuration, you
|
If you define your form types in the Dependency Injection configuration, you
|
||||||
should further remove the "alias" attribute:
|
should further remove the "alias" attribute:
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<service id="my.type" class="Vendor\Type\MyType">
|
<service id="my.type" class="Vendor\Type\MyType">
|
||||||
<tag name="form.type" alias="mytype" />
|
<tag name="form.type" alias="mytype" />
|
||||||
</service>
|
</service>
|
||||||
```
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<service id="my.type" class="Vendor\Type\MyType">
|
<service id="my.type" class="Vendor\Type\MyType">
|
||||||
<tag name="form.type" />
|
<tag name="form.type" />
|
||||||
</service>
|
</service>
|
||||||
```
|
```
|
||||||
|
|
||||||
Type extension should return the fully-qualified class name of the extended
|
Type extension should return the fully-qualified class name of the extended
|
||||||
type from `FormTypeExtensionInterface::getExtendedType()` now.
|
type from `FormTypeExtensionInterface::getExtendedType()` now.
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class MyTypeExtension extends AbstractTypeExtension
|
class MyTypeExtension extends AbstractTypeExtension
|
||||||
{
|
{
|
||||||
|
@ -155,12 +155,12 @@ Form
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||||
|
|
||||||
class MyTypeExtension extends AbstractTypeExtension
|
class MyTypeExtension extends AbstractTypeExtension
|
||||||
{
|
{
|
||||||
public function getExtendedType()
|
public function getExtendedType()
|
||||||
|
@ -169,14 +169,14 @@ Form
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If your extension has to be compatible with Symfony 2.3-2.8, use the
|
If your extension has to be compatible with Symfony 2.3-2.8, use the
|
||||||
following statement:
|
following statement:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||||
|
|
||||||
class MyTypeExtension extends AbstractTypeExtension
|
class MyTypeExtension extends AbstractTypeExtension
|
||||||
{
|
{
|
||||||
public function getExtendedType()
|
public function getExtendedType()
|
||||||
|
@ -185,13 +185,13 @@ Form
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
* Returning type instances from `FormTypeInterface::getParent()` is deprecated
|
* Returning type instances from `FormTypeInterface::getParent()` is deprecated
|
||||||
and will not be supported anymore in Symfony 3.0. Return the fully-qualified
|
and will not be supported anymore in Symfony 3.0. Return the fully-qualified
|
||||||
class name of the parent type class instead.
|
class name of the parent type class instead.
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class MyType
|
class MyType
|
||||||
{
|
{
|
||||||
|
@ -201,9 +201,9 @@ Form
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class MyType
|
class MyType
|
||||||
{
|
{
|
||||||
|
@ -213,24 +213,28 @@ Form
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
* Passing type instances to `Form::add()`, `FormBuilder::add()` and the
|
* Passing type instances to `Form::add()`, `FormBuilder::add()` and the
|
||||||
`FormFactory::create*()` methods is deprecated and will not be supported
|
`FormFactory::create*()` methods is deprecated and will not be supported
|
||||||
anymore in Symfony 3.0. Pass the fully-qualified class name of the type
|
anymore in Symfony 3.0. Pass the fully-qualified class name of the type
|
||||||
instead.
|
instead.
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$form = $this->createForm(new MyType());
|
$form = $this->createForm(new MyType());
|
||||||
```
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$form = $this->createForm(MyType::class);
|
$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
|
Translator
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ CHANGELOG
|
||||||
* deprecated the "cascade_validation" option in favor of setting "constraints"
|
* deprecated the "cascade_validation" option in favor of setting "constraints"
|
||||||
with the Valid constraint
|
with the Valid constraint
|
||||||
* moved data trimming logic of TrimListener into StringUtil
|
* 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
|
2.7.0
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -68,7 +68,18 @@ class DependencyInjectionExtension implements FormExtensionInterface
|
||||||
|
|
||||||
if (isset($this->typeExtensionServiceIds[$name])) {
|
if (isset($this->typeExtensionServiceIds[$name])) {
|
||||||
foreach ($this->typeExtensionServiceIds[$name] as $serviceId) {
|
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()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* 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');
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@
|
||||||
"symfony/phpunit-bridge": "~2.7|~3.0.0",
|
"symfony/phpunit-bridge": "~2.7|~3.0.0",
|
||||||
"doctrine/collections": "~1.0",
|
"doctrine/collections": "~1.0",
|
||||||
"symfony/validator": "~2.8|~3.0.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-foundation": "~2.2|~3.0.0",
|
||||||
"symfony/http-kernel": "~2.4|~3.0.0",
|
"symfony/http-kernel": "~2.4|~3.0.0",
|
||||||
"symfony/security-csrf": "~2.4|~3.0.0",
|
"symfony/security-csrf": "~2.4|~3.0.0",
|
||||||
|
|
Reference in New Issue