diff --git a/UPGRADE-4.2.md b/UPGRADE-4.2.md
index 31a06c8844f9..f04124d9bc4c 100644
--- a/UPGRADE-4.2.md
+++ b/UPGRADE-4.2.md
@@ -55,9 +55,38 @@ Finder
Form
----
+ * The `getExtendedType()` method of the `FormTypeExtensionInterface` is deprecated and will be removed in 5.0. Type
+ extensions must implement the static `getExtendedTypes()` method instead and return an iterable of extended types.
+
+ Before:
+
+ ```php
+ class FooTypeExtension extends AbstractTypeExtension
+ {
+ public function getExtendedType()
+ {
+ return FormType::class;
+ }
+
+ // ...
+ }
+ ```
+
+ After:
+
+ ```php
+ class FooTypeExtension extends AbstractTypeExtension
+ {
+ public static function getExtendedTypes(): iterable
+ {
+ return array(FormType::class);
+ }
+
+ // ...
+ }
+ ```
* The `scale` option of the `IntegerType` is deprecated.
* The `$scale` argument of the `IntegerToLocalizedStringTransformer` is deprecated.
-
* Deprecated calling `FormRenderer::searchAndRenderBlock` for fields which were already rendered.
Instead of expecting such calls to return empty strings, check if the field has already been rendered.
diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md
index d3ca51c806c2..80403f038d62 100644
--- a/UPGRADE-5.0.md
+++ b/UPGRADE-5.0.md
@@ -72,6 +72,36 @@ Finder
Form
----
+ * The `getExtendedType()` method was removed from the `FormTypeExtensionInterface`. It is replaced by the the static
+ `getExtendedTypes()` method which must return an iterable of extended types.
+
+ Before:
+
+ ```php
+ class FooTypeExtension extends AbstractTypeExtension
+ {
+ public function getExtendedType()
+ {
+ return FormType::class;
+ }
+
+ // ...
+ }
+ ```
+
+ After:
+
+ ```php
+ class FooTypeExtension extends AbstractTypeExtension
+ {
+ public static function getExtendedTypes(): iterable
+ {
+ return array(FormType::class);
+ }
+
+ // ...
+ }
+ ```
* The `scale` option was removed from the `IntegerType`.
* The `$scale` argument of the `IntegerToLocalizedStringTransformer` was removed.
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index d09b0afb9d49..ebae75ec3f05 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -52,6 +52,7 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\Finder\Finder;
+use Symfony\Component\Form\FormTypeExtensionInterface;
use Symfony\Component\Form\FormTypeGuesserInterface;
use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
@@ -325,6 +326,8 @@ public function load(array $configs, ContainerBuilder $container)
->addTag('form.type');
$container->registerForAutoconfiguration(FormTypeGuesserInterface::class)
->addTag('form.type_guesser');
+ $container->registerForAutoconfiguration(FormTypeExtensionInterface::class)
+ ->addTag('form.type_extension');
$container->registerForAutoconfiguration(CacheClearerInterface::class)
->addTag('kernel.cache_clearer');
$container->registerForAutoconfiguration(CacheWarmerInterface::class)
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
index 504fe619bf4d..898ab56a2efa 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
@@ -74,7 +74,7 @@
-
+
@@ -92,13 +92,13 @@
-
+
-
+
%validator.translation_domain%
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml
index 5acfce366d77..25b1cecc5a73 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml
@@ -8,7 +8,7 @@
-
+
%form.type_extension.csrf.enabled%
%form.type_extension.csrf.field_name%
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml
index 525b18c57b60..c8d35db32aaf 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml
@@ -16,7 +16,7 @@
-
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json
index ed47758fa000..6be5e16fa62a 100644
--- a/src/Symfony/Bundle/FrameworkBundle/composer.json
+++ b/src/Symfony/Bundle/FrameworkBundle/composer.json
@@ -39,7 +39,7 @@
"symfony/dom-crawler": "~3.4|~4.0",
"symfony/polyfill-intl-icu": "~1.0",
"symfony/security": "~3.4|~4.0",
- "symfony/form": "^4.1",
+ "symfony/form": "^4.2",
"symfony/expression-language": "~3.4|~4.0",
"symfony/messenger": "^4.2",
"symfony/process": "~3.4|~4.0",
@@ -66,7 +66,7 @@
"phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
"symfony/asset": "<3.4",
"symfony/console": "<3.4",
- "symfony/form": "<4.1",
+ "symfony/form": "<4.2",
"symfony/messenger": "<4.2",
"symfony/property-info": "<3.4",
"symfony/serializer": "<4.1",
diff --git a/src/Symfony/Component/Form/AbstractExtension.php b/src/Symfony/Component/Form/AbstractExtension.php
index 61497aba6b62..83dd8236035a 100644
--- a/src/Symfony/Component/Form/AbstractExtension.php
+++ b/src/Symfony/Component/Form/AbstractExtension.php
@@ -175,9 +175,21 @@ private function initTypeExtensions()
throw new UnexpectedTypeException($extension, 'Symfony\Component\Form\FormTypeExtensionInterface');
}
- $type = $extension->getExtendedType();
+ if (method_exists($extension, 'getExtendedTypes')) {
+ $extendedTypes = array();
- $this->typeExtensions[$type][] = $extension;
+ foreach ($extension::getExtendedTypes() as $extendedType) {
+ $extendedTypes[] = $extendedType;
+ }
+ } else {
+ @trigger_error(sprintf('Not implementing the static getExtendedTypes() method in %s when implementing the %s is deprecated since Symfony 4.2. The method will be added to the interface in 5.0.', \get_class($extension), FormTypeExtensionInterface::class), E_USER_DEPRECATED);
+
+ $extendedTypes = array($extension->getExtendedType());
+ }
+
+ foreach ($extendedTypes as $extendedType) {
+ $this->typeExtensions[$extendedType][] = $extension;
+ }
}
}
diff --git a/src/Symfony/Component/Form/AbstractTypeExtension.php b/src/Symfony/Component/Form/AbstractTypeExtension.php
index 9d369bf294e9..2305db89ee44 100644
--- a/src/Symfony/Component/Form/AbstractTypeExtension.php
+++ b/src/Symfony/Component/Form/AbstractTypeExtension.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Form;
+use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
@@ -45,4 +46,22 @@ public function finishView(FormView $view, FormInterface $form, array $options)
public function configureOptions(OptionsResolver $resolver)
{
}
+
+ /**
+ * {@inheritdoc}
+ *
+ * @deprecated since Symfony 4.2, use getExtendedTypes() instead.
+ */
+ public function getExtendedType()
+ {
+ if (!method_exists($this, 'getExtendedTypes')) {
+ throw new LogicException(sprintf('You need to implement the static getExtendedTypes() method when implementing the %s in %s.', FormTypeExtensionInterface::class, static::class));
+ }
+
+ @trigger_error(sprintf('The %s::getExtendedType() method is deprecated since Symfony 4.2 and will be removed in 5.0. Use getExtendedTypes() instead.', \get_class($this)), E_USER_DEPRECATED);
+
+ foreach (static::getExtendedTypes() as $extendedType) {
+ return $extendedType;
+ }
+ }
}
diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md
index 388d11652994..dc2ddf78db3c 100644
--- a/src/Symfony/Component/Form/CHANGELOG.md
+++ b/src/Symfony/Component/Form/CHANGELOG.md
@@ -4,6 +4,36 @@ CHANGELOG
4.2.0
-----
+ * The `getExtendedType()` method of the `FormTypeExtensionInterface` is deprecated and will be removed in 5.0. Type
+ extensions must implement the static `getExtendedTypes()` method instead and return an iterable of extended types.
+
+ Before:
+
+ ```php
+ class FooTypeExtension extends AbstractTypeExtension
+ {
+ public function getExtendedType()
+ {
+ return FormType::class;
+ }
+
+ // ...
+ }
+ ```
+
+ After:
+
+ ```php
+ class FooTypeExtension extends AbstractTypeExtension
+ {
+ public static function getExtendedTypes(): iterable
+ {
+ return array(FormType::class);
+ }
+
+ // ...
+ }
+ ```
* deprecated the `$scale` argument of the `IntegerToLocalizedStringTransformer`
* added `Symfony\Component\Form\ClearableErrorsInterface`
* deprecated calling `FormRenderer::searchAndRenderBlock` for fields which were already rendered
diff --git a/src/Symfony/Component/Form/DependencyInjection/FormPass.php b/src/Symfony/Component/Form/DependencyInjection/FormPass.php
index 01a81469503b..8b604ccebc1b 100644
--- a/src/Symfony/Component/Form/DependencyInjection/FormPass.php
+++ b/src/Symfony/Component/Form/DependencyInjection/FormPass.php
@@ -18,6 +18,7 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\Form\FormTypeExtensionInterface;
/**
* Adds all services with the tags "form.type", "form.type_extension" and
@@ -92,13 +93,27 @@ private function processFormTypeExtensions(ContainerBuilder $container)
$tag = $serviceDefinition->getTag($this->formTypeExtensionTag);
if (isset($tag[0]['extended_type'])) {
- $extendedType = $tag[0]['extended_type'];
+ if (!method_exists($serviceDefinition->getClass(), 'getExtendedTypes')) {
+ @trigger_error(sprintf('Not implementing the static getExtendedTypes() method in %s when implementing the %s is deprecated since Symfony 4.2. The method will be added to the interface in 5.0.', $serviceDefinition->getClass(), FormTypeExtensionInterface::class), E_USER_DEPRECATED);
+ }
+
+ $typeExtensions[$tag[0]['extended_type']][] = new Reference($serviceId);
+ $typeExtensionsClasses[] = $serviceDefinition->getClass();
+ } elseif (method_exists($serviceDefinition->getClass(), 'getExtendedTypes')) {
+ $extendsTypes = false;
+
+ foreach ($serviceDefinition->getClass()::getExtendedTypes() as $extendedType) {
+ $typeExtensions[$extendedType][] = new Reference($serviceId);
+ $typeExtensionsClasses[] = $serviceDefinition->getClass();
+ $extendsTypes = true;
+ }
+
+ if (!$extendsTypes) {
+ throw new InvalidArgumentException(sprintf('The getExtendedTypes() method for service "%s" does not return any extended types.', $serviceId));
+ }
} else {
- 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));
+ throw new InvalidArgumentException(sprintf('"%s" tagged services have to implement the static getExtendedTypes() method. The class for service "%s" does not implement it.', $this->formTypeExtensionTag, $serviceId));
}
-
- $typeExtensions[$extendedType][] = new Reference($serviceId);
- $typeExtensionsClasses[] = $serviceDefinition->getClass();
}
foreach ($typeExtensions as $extendedType => $extensions) {
diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php
index 7e16f6dc375f..4a39b1d999a9 100644
--- a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php
+++ b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\Csrf\Type;
use Symfony\Component\Form\AbstractTypeExtension;
+use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
@@ -114,8 +115,8 @@ public function configureOptions(OptionsResolver $resolver)
/**
* {@inheritdoc}
*/
- public function getExtendedType()
+ public static function getExtendedTypes(): iterable
{
- return 'Symfony\Component\Form\Extension\Core\Type\FormType';
+ return array(FormType::class);
}
}
diff --git a/src/Symfony/Component/Form/Extension/DataCollector/Type/DataCollectorTypeExtension.php b/src/Symfony/Component/Form/Extension/DataCollector/Type/DataCollectorTypeExtension.php
index 5c4076d06095..5dffe481cc8b 100644
--- a/src/Symfony/Component/Form/Extension/DataCollector/Type/DataCollectorTypeExtension.php
+++ b/src/Symfony/Component/Form/Extension/DataCollector/Type/DataCollectorTypeExtension.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\DataCollector\Type;
use Symfony\Component\Form\AbstractTypeExtension;
+use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\DataCollector\EventListener\DataCollectorListener;
use Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface;
use Symfony\Component\Form\FormBuilderInterface;
@@ -45,8 +46,8 @@ public function buildForm(FormBuilderInterface $builder, array $options)
/**
* {@inheritdoc}
*/
- public function getExtendedType()
+ public static function getExtendedTypes(): iterable
{
- return 'Symfony\Component\Form\Extension\Core\Type\FormType';
+ return array(FormType::class);
}
}
diff --git a/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php b/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php
index a97f6af1a2ef..7afccddf0f81 100644
--- a/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php
+++ b/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php
@@ -58,9 +58,19 @@ public function getTypeExtensions($name)
foreach ($this->typeExtensionServices[$name] as $serviceId => $extension) {
$extensions[] = $extension;
- // 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()));
+ if (method_exists($extension, 'getExtendedTypes')) {
+ $extendedTypes = array();
+
+ foreach ($extension::getExtendedTypes() as $extendedType) {
+ $extendedTypes[] = $extendedType;
+ }
+ } else {
+ $extendedTypes = array($extension->getExtendedType());
+ }
+
+ // validate the result of getExtendedTypes()/getExtendedType() to ensure it is consistent with the service definition
+ if (!\in_array($name, $extendedTypes, true)) {
+ 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, implode(', ', $extendedTypes)));
}
}
}
diff --git a/src/Symfony/Component/Form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php b/src/Symfony/Component/Form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php
index 0216b41c1271..c3551d55e02b 100644
--- a/src/Symfony/Component/Form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php
+++ b/src/Symfony/Component/Form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\HttpFoundation\Type;
use Symfony\Component\Form\AbstractTypeExtension;
+use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\RequestHandlerInterface;
@@ -39,8 +40,8 @@ public function buildForm(FormBuilderInterface $builder, array $options)
/**
* {@inheritdoc}
*/
- public function getExtendedType()
+ public static function getExtendedTypes(): iterable
{
- return 'Symfony\Component\Form\Extension\Core\Type\FormType';
+ return array(FormType::class);
}
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php
index b83c2030c0f0..418950aa7d94 100644
--- a/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php
+++ b/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Form\Extension\Validator\Type;
+use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener;
use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper;
use Symfony\Component\Form\FormBuilderInterface;
@@ -67,8 +68,8 @@ public function configureOptions(OptionsResolver $resolver)
/**
* {@inheritdoc}
*/
- public function getExtendedType()
+ public static function getExtendedTypes(): iterable
{
- return 'Symfony\Component\Form\Extension\Core\Type\FormType';
+ return array(FormType::class);
}
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php
index ff27fbdb474f..3ca6c5308a41 100644
--- a/src/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php
+++ b/src/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\Validator\Type;
use Symfony\Component\Form\AbstractTypeExtension;
+use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -38,8 +39,8 @@ public function configureOptions(OptionsResolver $resolver)
/**
* {@inheritdoc}
*/
- public function getExtendedType()
+ public static function getExtendedTypes(): iterable
{
- return 'Symfony\Component\Form\Extension\Core\Type\RepeatedType';
+ return array(RepeatedType::class);
}
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php
index fa844eb1331a..ef14faa99b30 100644
--- a/src/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php
+++ b/src/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\Form\Extension\Validator\Type;
+use Symfony\Component\Form\Extension\Core\Type\SubmitType;
+
/**
* @author Bernhard Schussek
*/
@@ -19,8 +21,8 @@ class SubmitTypeValidatorExtension extends BaseValidatorExtension
/**
* {@inheritdoc}
*/
- public function getExtendedType()
+ public static function getExtendedTypes(): iterable
{
- return 'Symfony\Component\Form\Extension\Core\Type\SubmitType';
+ return array(SubmitType::class);
}
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php
index 9e5eec80014f..54f7db095db7 100644
--- a/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php
+++ b/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\Validator\Type;
use Symfony\Component\Form\AbstractTypeExtension;
+use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
@@ -55,8 +56,8 @@ public function configureOptions(OptionsResolver $resolver)
/**
* {@inheritdoc}
*/
- public function getExtendedType()
+ public static function getExtendedTypes(): iterable
{
- return 'Symfony\Component\Form\Extension\Core\Type\FormType';
+ return array(FormType::class);
}
}
diff --git a/src/Symfony/Component/Form/FormFactoryBuilder.php b/src/Symfony/Component/Form/FormFactoryBuilder.php
index 3afe1f535960..59f5f4910d73 100644
--- a/src/Symfony/Component/Form/FormFactoryBuilder.php
+++ b/src/Symfony/Component/Form/FormFactoryBuilder.php
@@ -100,7 +100,13 @@ public function addTypes(array $types)
*/
public function addTypeExtension(FormTypeExtensionInterface $typeExtension)
{
- $this->typeExtensions[$typeExtension->getExtendedType()][] = $typeExtension;
+ if (method_exists($typeExtension, 'getExtendedTypes')) {
+ foreach ($typeExtension::getExtendedTypes() as $extendedType) {
+ $this->typeExtensions[$extendedType][] = $typeExtension;
+ }
+ } else {
+ $this->typeExtensions[$typeExtension->getExtendedType()][] = $typeExtension;
+ }
return $this;
}
@@ -111,7 +117,7 @@ public function addTypeExtension(FormTypeExtensionInterface $typeExtension)
public function addTypeExtensions(array $typeExtensions)
{
foreach ($typeExtensions as $typeExtension) {
- $this->typeExtensions[$typeExtension->getExtendedType()][] = $typeExtension;
+ $this->addTypeExtension($typeExtension);
}
return $this;
diff --git a/src/Symfony/Component/Form/FormTypeExtensionInterface.php b/src/Symfony/Component/Form/FormTypeExtensionInterface.php
index f7394a2999a1..c80d8a837160 100644
--- a/src/Symfony/Component/Form/FormTypeExtensionInterface.php
+++ b/src/Symfony/Component/Form/FormTypeExtensionInterface.php
@@ -15,6 +15,8 @@
/**
* @author Bernhard Schussek
+ *
+ * @method getExtendedTypes(): iterable
*/
interface FormTypeExtensionInterface
{
@@ -59,6 +61,8 @@ public function configureOptions(OptionsResolver $resolver);
* Returns the name of the type being extended.
*
* @return string The name of the type being extended
+ *
+ * @deprecated since Symfony 4.2, use getExtendedTypes() instead.
*/
public function getExtendedType();
}
diff --git a/src/Symfony/Component/Form/PreloadedExtension.php b/src/Symfony/Component/Form/PreloadedExtension.php
index b26dbe638d8c..4b1f3e521370 100644
--- a/src/Symfony/Component/Form/PreloadedExtension.php
+++ b/src/Symfony/Component/Form/PreloadedExtension.php
@@ -33,6 +33,14 @@ class PreloadedExtension implements FormExtensionInterface
*/
public function __construct(array $types, array $typeExtensions, FormTypeGuesserInterface $typeGuesser = null)
{
+ foreach ($typeExtensions as $extensions) {
+ foreach ($extensions as $typeExtension) {
+ if (!method_exists($typeExtension, 'getExtendedTypes')) {
+ @trigger_error(sprintf('Not implementing the static getExtendedTypes() method in %s when implementing the %s is deprecated since Symfony 4.2. The method will be added to the interface in 5.0.', \get_class($typeExtension), FormTypeExtensionInterface::class), E_USER_DEPRECATED);
+ }
+ }
+ }
+
$this->typeExtensions = $typeExtensions;
$this->typeGuesser = $typeGuesser;
diff --git a/src/Symfony/Component/Form/Tests/AbstractTypeExtensionTest.php b/src/Symfony/Component/Form/Tests/AbstractTypeExtensionTest.php
new file mode 100644
index 000000000000..73b066a2076c
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/AbstractTypeExtensionTest.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Form\AbstractTypeExtension;
+use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
+use Symfony\Component\Form\Extension\Core\Type\DateType;
+
+class AbstractTypeExtensionTest extends TestCase
+{
+ /**
+ * @expectedException \Symfony\Component\Form\Exception\LogicException
+ * @expectedExceptionMessage You need to implement the static getExtendedTypes() method when implementing the Symfony\Component\Form\FormTypeExtensionInterface in Symfony\Component\Form\Tests\TypeExtensionWithoutExtendedTypes.
+ */
+ public function testImplementingNeitherGetExtendedTypeNorExtendsTypeThrowsException()
+ {
+ $extension = new TypeExtensionWithoutExtendedTypes();
+ $extension->getExtendedType();
+ }
+
+ /**
+ * @group legacy
+ */
+ public function testGetExtendedTypeReturnsFirstConfiguredExtension()
+ {
+ $extension = new MultipleTypesExtension();
+
+ $this->assertSame(DateTimeType::class, $extension->getExtendedType());
+ }
+}
+
+class MultipleTypesExtension extends AbstractTypeExtension
+{
+ public static function getExtendedTypes(): iterable
+ {
+ yield DateTimeType::class;
+ yield DateType::class;
+ }
+}
+
+class TypeExtensionWithoutExtendedTypes extends AbstractTypeExtension
+{
+}
diff --git a/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php b/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php
index f5939b40e09c..debbddfdb3f1 100644
--- a/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php
+++ b/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php
@@ -19,6 +19,7 @@
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\DependencyInjection\FormPass;
use Symfony\Component\Form\FormRegistryInterface;
@@ -97,6 +98,107 @@ public function testAddTaggedTypeExtensions(array $extensions, array $expectedRe
$container->setDefinition('form.extension', $this->createExtensionDefinition());
+ foreach ($extensions as $serviceId => $config) {
+ $container->register($serviceId, $config['class'])->addTag('form.type_extension', $config['tag']);
+ }
+
+ $container->compile();
+
+ $extDefinition = $container->getDefinition('form.extension');
+ $this->assertEquals($expectedRegisteredExtensions, $extDefinition->getArgument(1));
+ }
+
+ public function addTaggedTypeExtensionsDataProvider()
+ {
+ return array(
+ array(
+ array(
+ Type1TypeExtension::class => array(
+ 'class' => Type1TypeExtension::class,
+ 'tag' => array('extended_type' => 'type1'),
+ ),
+ Type1Type2TypeExtension::class => array(
+ 'class' => Type1Type2TypeExtension::class,
+ 'tag' => array('extended_type' => 'type2'),
+ ),
+ ),
+ array(
+ 'type1' => new IteratorArgument(array(new Reference(Type1TypeExtension::class))),
+ 'type2' => new IteratorArgument(array(new Reference(Type1Type2TypeExtension::class))),
+ ),
+ ),
+ array(
+ array(
+ Type1TypeExtension::class => array(
+ 'class' => Type1TypeExtension::class,
+ 'tag' => array(),
+ ),
+ Type1Type2TypeExtension::class => array(
+ 'class' => Type1Type2TypeExtension::class,
+ 'tag' => array(),
+ ),
+ ),
+ array(
+ 'type1' => new IteratorArgument(array(
+ new Reference(Type1TypeExtension::class),
+ new Reference(Type1Type2TypeExtension::class),
+ )),
+ 'type2' => new IteratorArgument(array(new Reference(Type1Type2TypeExtension::class))),
+ ),
+ ),
+ array(
+ array(
+ 'my.type_extension1' => array(
+ 'class' => Type1TypeExtension::class,
+ 'tag' => array('extended_type' => 'type1', 'priority' => 1),
+ ),
+ 'my.type_extension2' => array(
+ 'class' => Type1TypeExtension::class,
+ 'tag' => array('extended_type' => 'type1', 'priority' => 2),
+ ),
+ 'my.type_extension3' => array(
+ 'class' => Type1TypeExtension::class,
+ 'tag' => array('extended_type' => 'type1', 'priority' => -1),
+ ),
+ 'my.type_extension4' => array(
+ 'class' => Type2TypeExtension::class,
+ 'tag' => array('extended_type' => 'type2', 'priority' => 2),
+ ),
+ 'my.type_extension5' => array(
+ 'class' => Type2TypeExtension::class,
+ 'tag' => array('extended_type' => 'type2', 'priority' => 1),
+ ),
+ 'my.type_extension6' => array(
+ 'class' => Type2TypeExtension::class,
+ 'tag' => array('extended_type' => 'type2', 'priority' => 1),
+ ),
+ ),
+ array(
+ 'type1' => new IteratorArgument(array(
+ new Reference('my.type_extension2'),
+ new Reference('my.type_extension1'),
+ new Reference('my.type_extension3'),
+ )),
+ 'type2' => new IteratorArgument(array(
+ new Reference('my.type_extension4'),
+ new Reference('my.type_extension5'),
+ new Reference('my.type_extension6'),
+ )),
+ ),
+ ),
+ );
+ }
+
+ /**
+ * @group legacy
+ * @dataProvider addLegacyTaggedTypeExtensionsDataProvider
+ */
+ public function testAddLegacyTaggedTypeExtensions(array $extensions, array $expectedRegisteredExtensions)
+ {
+ $container = $this->createContainerBuilder();
+
+ $container->setDefinition('form.extension', $this->createExtensionDefinition());
+
foreach ($extensions as $serviceId => $tag) {
$container->register($serviceId, 'stdClass')->addTag('form.type_extension', $tag);
}
@@ -110,7 +212,7 @@ public function testAddTaggedTypeExtensions(array $extensions, array $expectedRe
/**
* @return array
*/
- public function addTaggedTypeExtensionsDataProvider()
+ public function addLegacyTaggedTypeExtensionsDataProvider()
{
return array(
array(
@@ -154,9 +256,9 @@ public function addTaggedTypeExtensionsDataProvider()
/**
* @expectedException \InvalidArgumentException
- * @expectedExceptionMessage extended-type attribute, none was configured for the "my.type_extension" service
+ * @expectedExceptionMessage "form.type_extension" tagged services have to implement the static getExtendedTypes() method. The class for service "my.type_extension" does not implement it.
*/
- public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttribute()
+ public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttributeNorImplementingGetExtendedTypes()
{
$container = $this->createContainerBuilder();
@@ -168,6 +270,22 @@ public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttribute()
$container->compile();
}
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage The getExtendedTypes() method for service "my.type_extension" does not return any extended types.
+ */
+ public function testAddTaggedFormTypeExtensionWithoutExtendingAnyType()
+ {
+ $container = $this->createContainerBuilder();
+
+ $container->setDefinition('form.extension', $this->createExtensionDefinition());
+ $container->register('my.type_extension', WithoutExtendedTypesTypeExtension::class)
+ ->setPublic(true)
+ ->addTag('form.type_extension');
+
+ $container->compile();
+ }
+
public function testAddTaggedGuessers()
{
$container = $this->createContainerBuilder();
@@ -197,13 +315,13 @@ public function testAddTaggedGuessers()
/**
* @dataProvider privateTaggedServicesProvider
*/
- public function testPrivateTaggedServices($id, $tagName, callable $assertion, array $tagAttributes = array())
+ public function testPrivateTaggedServices($id, $class, $tagName, callable $assertion, array $tagAttributes = array())
{
$formPass = new FormPass();
$container = new ContainerBuilder();
$container->setDefinition('form.extension', $this->createExtensionDefinition());
- $container->register($id, 'stdClass')->setPublic(false)->addTag($tagName, $tagAttributes);
+ $container->register($id, $class)->setPublic(false)->addTag($tagName, $tagAttributes);
$formPass->process($container);
$assertion($container);
@@ -214,6 +332,7 @@ public function privateTaggedServicesProvider()
return array(
array(
'my.type',
+ 'stdClass',
'form.type',
function (ContainerBuilder $container) {
$formTypes = $container->getDefinition('form.extension')->getArgument(0);
@@ -231,6 +350,7 @@ function (ContainerBuilder $container) {
),
array(
'my.type_extension',
+ Type1TypeExtension::class,
'form.type_extension',
function (ContainerBuilder $container) {
$this->assertEquals(
@@ -240,7 +360,7 @@ function (ContainerBuilder $container) {
},
array('extended_type' => 'Symfony\Component\Form\Extension\Core\Type\FormType'),
),
- array('my.guesser', 'form.type_guesser', function (ContainerBuilder $container) {
+ array('my.guesser', 'stdClass', 'form.type_guesser', function (ContainerBuilder $container) {
$this->assertEquals(new IteratorArgument(array(new Reference('my.guesser'))), $container->getDefinition('form.extension')->getArgument(2));
}),
);
@@ -288,3 +408,36 @@ class FormPassTest_Type1 extends AbstractType
class FormPassTest_Type2 extends AbstractType
{
}
+
+class Type1TypeExtension extends AbstractTypeExtension
+{
+ public static function getExtendedTypes(): iterable
+ {
+ return array('type1');
+ }
+}
+
+class Type2TypeExtension extends AbstractTypeExtension
+{
+ public static function getExtendedTypes(): iterable
+ {
+ return array('type2');
+ }
+}
+
+class Type1Type2TypeExtension extends AbstractTypeExtension
+{
+ public static function getExtendedTypes(): iterable
+ {
+ yield 'type1';
+ yield 'type2';
+ }
+}
+
+class WithoutExtendedTypesTypeExtension extends AbstractTypeExtension
+{
+ public static function getExtendedTypes(): iterable
+ {
+ return array();
+ }
+}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ExtendedChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ExtendedChoiceTypeTest.php
index 0475254d970a..7d33e7e3903d 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ExtendedChoiceTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ExtendedChoiceTypeTest.php
@@ -23,8 +23,9 @@ class ExtendedChoiceTypeTest extends TestCase
*/
public function testChoicesAreOverridden($type)
{
+ ChoiceTypeExtension::$extendedType = $type;
$factory = Forms::createFormFactoryBuilder()
- ->addTypeExtension(new ChoiceTypeExtension($type))
+ ->addTypeExtension(new ChoiceTypeExtension())
->getFormFactory()
;
@@ -42,8 +43,9 @@ public function testChoicesAreOverridden($type)
*/
public function testChoiceLoaderIsOverridden($type)
{
+ LazyChoiceTypeExtension::$extendedType = $type;
$factory = Forms::createFormFactoryBuilder()
- ->addTypeExtension(new LazyChoiceTypeExtension($type))
+ ->addTypeExtension(new LazyChoiceTypeExtension())
->getFormFactory()
;
diff --git a/src/Symfony/Component/Form/Tests/Extension/DataCollector/Type/DataCollectorTypeExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/DataCollector/Type/DataCollectorTypeExtensionTest.php
index de635f64a3f4..88561b3bb59e 100644
--- a/src/Symfony/Component/Form/Tests/Extension/DataCollector/Type/DataCollectorTypeExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/DataCollector/Type/DataCollectorTypeExtensionTest.php
@@ -32,6 +32,9 @@ protected function setUp()
$this->extension = new DataCollectorTypeExtension($this->dataCollector);
}
+ /**
+ * @group legacy
+ */
public function testGetExtendedType()
{
$this->assertEquals('Symfony\Component\Form\Extension\Core\Type\FormType', $this->extension->getExtendedType());
diff --git a/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php
index a94d57ec475a..9d8020b07836 100644
--- a/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests\Extension\DependencyInjection;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension;
use Symfony\Component\Form\FormTypeGuesserChain;
use Symfony\Component\Form\FormTypeGuesserInterface;
@@ -23,13 +24,14 @@ public function testGetTypeExtensions()
$container = $this->createContainerMock();
$container->expects($this->never())->method('get');
- $typeExtension1 = $this->createFormTypeExtensionMock('test');
- $typeExtension2 = $this->createFormTypeExtensionMock('test');
- $typeExtension3 = $this->createFormTypeExtensionMock('other');
+ $typeExtension1 = new TestTypeExtension();
+ $typeExtension2 = new TestTypeExtension();
+ $typeExtension3 = new OtherTypeExtension();
+ $typeExtension4 = new MultipleTypesTypeExtension();
$extensions = array(
- 'test' => new \ArrayIterator(array($typeExtension1, $typeExtension2)),
- 'other' => new \ArrayIterator(array($typeExtension3)),
+ 'test' => new \ArrayIterator(array($typeExtension1, $typeExtension2, $typeExtension4)),
+ 'other' => new \ArrayIterator(array($typeExtension3, $typeExtension4)),
);
$extension = new DependencyInjectionExtension($container, $extensions, array());
@@ -37,8 +39,8 @@ public function testGetTypeExtensions()
$this->assertTrue($extension->hasTypeExtensions('test'));
$this->assertTrue($extension->hasTypeExtensions('other'));
$this->assertFalse($extension->hasTypeExtensions('unknown'));
- $this->assertSame(array($typeExtension1, $typeExtension2), $extension->getTypeExtensions('test'));
- $this->assertSame(array($typeExtension3), $extension->getTypeExtensions('other'));
+ $this->assertSame(array($typeExtension1, $typeExtension2, $typeExtension4), $extension->getTypeExtensions('test'));
+ $this->assertSame(array($typeExtension3, $typeExtension4), $extension->getTypeExtensions('other'));
}
/**
@@ -50,12 +52,12 @@ public function testThrowExceptionForInvalidExtendedType()
$container->expects($this->never())->method('get');
$extensions = array(
- 'test' => new \ArrayIterator(array($this->createFormTypeExtensionMock('unmatched'))),
+ 'unmatched' => new \ArrayIterator(array(new TestTypeExtension())),
);
$extension = new DependencyInjectionExtension($container, $extensions, array());
- $extension->getTypeExtensions('test');
+ $extension->getTypeExtensions('unmatched');
}
public function testGetTypeGuesser()
@@ -80,12 +82,29 @@ private function createContainerMock()
->setMethods(array('get', 'has'))
->getMock();
}
+}
- private function createFormTypeExtensionMock($extendedType)
+class TestTypeExtension extends AbstractTypeExtension
+{
+ public static function getExtendedTypes(): iterable
{
- $extension = $this->getMockBuilder('Symfony\Component\Form\FormTypeExtensionInterface')->getMock();
- $extension->expects($this->any())->method('getExtendedType')->willReturn($extendedType);
+ return array('test');
+ }
+}
- return $extension;
+class OtherTypeExtension extends AbstractTypeExtension
+{
+ public static function getExtendedTypes(): iterable
+ {
+ return array('other');
+ }
+}
+
+class MultipleTypesTypeExtension extends AbstractTypeExtension
+{
+ public static function getExtendedTypes(): iterable
+ {
+ yield 'test';
+ yield 'other';
}
}
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/ChoiceTypeExtension.php b/src/Symfony/Component/Form/Tests/Fixtures/ChoiceTypeExtension.php
index db4166ff3092..55e394a2469e 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/ChoiceTypeExtension.php
+++ b/src/Symfony/Component/Form/Tests/Fixtures/ChoiceTypeExtension.php
@@ -12,17 +12,11 @@
namespace Symfony\Component\Form\Tests\Fixtures;
use Symfony\Component\Form\AbstractTypeExtension;
-use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ChoiceTypeExtension extends AbstractTypeExtension
{
- private $extendedType;
-
- public function __construct($extendedType = ChoiceType::class)
- {
- $this->extendedType = $extendedType;
- }
+ public static $extendedType;
/**
* {@inheritdoc}
@@ -38,8 +32,8 @@ public function configureOptions(OptionsResolver $resolver)
/**
* {@inheritdoc}
*/
- public function getExtendedType()
+ public static function getExtendedTypes(): iterable
{
- return $this->extendedType;
+ return array(self::$extendedType);
}
}
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBarExtension.php b/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBarExtension.php
index 8d72132f2f1f..5ed877fdf99c 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBarExtension.php
+++ b/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBarExtension.php
@@ -28,8 +28,8 @@ public function getAllowedOptionValues()
);
}
- public function getExtendedType()
+ public static function getExtendedTypes(): iterable
{
- return __NAMESPACE__.'\FooType';
+ return array(__NAMESPACE__.'\FooType');
}
}
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBazExtension.php b/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBazExtension.php
index 646d41f7ffe0..1f722776e02f 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBazExtension.php
+++ b/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBazExtension.php
@@ -21,8 +21,8 @@ public function buildForm(FormBuilderInterface $builder, array $options)
$builder->setAttribute('baz', 'x');
}
- public function getExtendedType()
+ public static function getExtendedTypes(): iterable
{
- return __NAMESPACE__.'\FooType';
+ return array(__NAMESPACE__.'\FooType');
}
}
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/LazyChoiceTypeExtension.php b/src/Symfony/Component/Form/Tests/Fixtures/LazyChoiceTypeExtension.php
index 133b8829e0df..66af47aa17ce 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/LazyChoiceTypeExtension.php
+++ b/src/Symfony/Component/Form/Tests/Fixtures/LazyChoiceTypeExtension.php
@@ -13,17 +13,11 @@
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
-use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class LazyChoiceTypeExtension extends AbstractTypeExtension
{
- private $extendedType;
-
- public function __construct($extendedType = ChoiceType::class)
- {
- $this->extendedType = $extendedType;
- }
+ public static $extendedType;
/**
* {@inheritdoc}
@@ -41,8 +35,8 @@ public function configureOptions(OptionsResolver $resolver)
/**
* {@inheritdoc}
*/
- public function getExtendedType()
+ public static function getExtendedTypes(): iterable
{
- return $this->extendedType;
+ return array(self::$extendedType);
}
}
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/TestExtension.php b/src/Symfony/Component/Form/Tests/Fixtures/TestExtension.php
index bdbc5a9c1cfb..0173bae6f9dc 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/TestExtension.php
+++ b/src/Symfony/Component/Form/Tests/Fixtures/TestExtension.php
@@ -46,13 +46,13 @@ public function hasType($name)
public function addTypeExtension(FormTypeExtensionInterface $extension)
{
- $type = $extension->getExtendedType();
+ foreach ($extension::getExtendedTypes() as $type) {
+ if (!isset($this->extensions[$type])) {
+ $this->extensions[$type] = array();
+ }
- if (!isset($this->extensions[$type])) {
- $this->extensions[$type] = array();
+ $this->extensions[$type][] = $extension;
}
-
- $this->extensions[$type][] = $extension;
}
public function getTypeExtensions($name)