From 600e75ce8894f5d18f1726d9d45241f83074de44 Mon Sep 17 00:00:00 2001 From: Hugo Hamon Date: Mon, 20 Feb 2017 16:13:02 +0100 Subject: [PATCH] [Form] use new service locator in DependencyInjectionExtension class, so that form types can be made private at some point. --- .../FrameworkBundle/Resources/config/form.xml | 40 +++-- .../Resources/config/form_csrf.xml | 10 +- .../Resources/config/form_debug.xml | 2 +- .../Compiler/FormPassTest.php | 148 ++++++++--------- .../Form/DependencyInjection/FormPass.php | 70 ++++---- .../DependencyInjectionExtension.php | 77 ++++++--- .../DependencyInjection/FormPassTest.php | 149 ++++++++---------- .../DependencyInjectionExtensionTest.php | 95 ++++++++--- src/Symfony/Component/Form/composer.json | 4 +- 9 files changed, 341 insertions(+), 254 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml index d26c00cd50..7b63c38790 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -30,17 +30,13 @@ - - - - - - - + + + - + @@ -61,7 +57,7 @@ - + @@ -71,7 +67,7 @@ The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. - + @@ -158,7 +154,7 @@ - + @@ -173,20 +169,34 @@ - + - + - + - + %validator.translation_domain% + + + + + + + + + + + + + The service "%service_id%" is internal and deprecated since Symfony 3.3 and will be removed in Symfony 4.0 + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml index f5417c1f0d..e4363184b9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + %form.type_extension.csrf.enabled% @@ -14,5 +14,13 @@ %validator.translation_domain% + + + + + + The service "%service_id%" is internal and deprecated since Symfony 3.3 and will be removed in Symfony 4.0 + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml index 843fc72bdb..00e7c34e5e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml @@ -13,7 +13,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php index 023afb3209..15c4ff9c9f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php @@ -13,6 +13,8 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; @@ -27,8 +29,7 @@ class FormPassTest extends TestCase { public function testDoNothingIfFormExtensionNotLoaded() { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); + $container = $this->createContainerBuilder(); $container->compile(); @@ -37,18 +38,9 @@ class FormPassTest extends TestCase public function testAddTaggedTypes() { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); + $container = $this->createContainerBuilder(); - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); - $extDefinition->setArguments(array( - new Reference('service_container'), - array(), - array(), - array(), - )); - - $container->setDefinition('form.extension', $extDefinition); + $container->setDefinition('form.extension', $this->createExtensionDefinition()); $container->register('my.type1', __CLASS__.'_Type1')->addTag('form.type'); $container->register('my.type2', __CLASS__.'_Type2')->addTag('form.type'); @@ -56,10 +48,13 @@ class FormPassTest extends TestCase $extDefinition = $container->getDefinition('form.extension'); - $this->assertEquals(array( - __CLASS__.'_Type1' => 'my.type1', - __CLASS__.'_Type2' => 'my.type2', - ), $extDefinition->getArgument(1)); + $this->assertEquals( + new ServiceLocatorArgument(array( + __CLASS__.'_Type1' => new Reference('my.type1'), + __CLASS__.'_Type2' => new Reference('my.type2'), + )), + $extDefinition->getArgument(0) + ); } /** @@ -67,17 +62,9 @@ class FormPassTest extends TestCase */ public function testAddTaggedTypeExtensions(array $extensions, array $expectedRegisteredExtensions) { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); + $container = $this->createContainerBuilder(); - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array( - new Reference('service_container'), - array(), - array(), - array(), - )); - - $container->setDefinition('form.extension', $extDefinition); + $container->setDefinition('form.extension', $this->createExtensionDefinition()); foreach ($extensions as $serviceId => $tag) { $container->register($serviceId, 'stdClass')->addTag('form.type_extension', $tag); @@ -86,7 +73,7 @@ class FormPassTest extends TestCase $container->compile(); $extDefinition = $container->getDefinition('form.extension'); - $this->assertSame($expectedRegisteredExtensions, $extDefinition->getArgument(2)); + $this->assertEquals($expectedRegisteredExtensions, $extDefinition->getArgument(1)); } /** @@ -102,8 +89,11 @@ class FormPassTest extends TestCase 'my.type_extension3' => array('extended_type' => 'type2'), ), array( - 'type1' => array('my.type_extension1', 'my.type_extension2'), - 'type2' => array('my.type_extension3'), + 'type1' => new IteratorArgument(array( + new Reference('my.type_extension1'), + new Reference('my.type_extension2'), + )), + 'type2' => new IteratorArgument(array(new Reference('my.type_extension3'))), ), ), array( @@ -116,8 +106,16 @@ class FormPassTest extends TestCase 'my.type_extension6' => array('extended_type' => 'type2', 'priority' => 1), ), array( - 'type1' => array('my.type_extension2', 'my.type_extension1', 'my.type_extension3'), - 'type2' => array('my.type_extension4', 'my.type_extension5', 'my.type_extension6'), + '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'), + )), ), ), ); @@ -129,17 +127,9 @@ class FormPassTest extends TestCase */ public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttribute() { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); + $container = $this->createContainerBuilder(); - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array( - new Reference('service_container'), - array(), - array(), - array(), - )); - - $container->setDefinition('form.extension', $extDefinition); + $container->setDefinition('form.extension', $this->createExtensionDefinition()); $container->register('my.type_extension', 'stdClass') ->addTag('form.type_extension'); @@ -148,23 +138,14 @@ class FormPassTest extends TestCase public function testAddTaggedGuessers() { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); - - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); - $extDefinition->setArguments(array( - new Reference('service_container'), - array(), - array(), - array(), - )); + $container = $this->createContainerBuilder(); $definition1 = new Definition('stdClass'); $definition1->addTag('form.type_guesser'); $definition2 = new Definition('stdClass'); $definition2->addTag('form.type_guesser'); - $container->setDefinition('form.extension', $extDefinition); + $container->setDefinition('form.extension', $this->createExtensionDefinition()); $container->setDefinition('my.guesser1', $definition1); $container->setDefinition('my.guesser2', $definition2); @@ -172,37 +153,24 @@ class FormPassTest extends TestCase $extDefinition = $container->getDefinition('form.extension'); - $this->assertSame(array( - 'my.guesser1', - 'my.guesser2', - ), $extDefinition->getArgument(3)); + $this->assertEquals( + new IteratorArgument(array( + new Reference('my.guesser1'), + new Reference('my.guesser2'), + )), + $extDefinition->getArgument(2) + ); } /** * @dataProvider privateTaggedServicesProvider */ - public function testPrivateTaggedServices($id, $tagName, $expectedExceptionMessage) + public function testPrivateTaggedServices($id, $tagName, array $tagAttributes = array()) { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); + $container = $this->createContainerBuilder(); - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); - $extDefinition->setArguments(array( - new Reference('service_container'), - array(), - array(), - array(), - )); - - $container->setDefinition('form.extension', $extDefinition); - $container->register($id, 'stdClass')->setPublic(false)->addTag($tagName); - - if (method_exists($this, 'expectException')) { - $this->expectException('InvalidArgumentException'); - $this->expectExceptionMessage($expectedExceptionMessage); - } else { - $this->setExpectedException('InvalidArgumentException', $expectedExceptionMessage); - } + $container->setDefinition('form.extension', $this->createExtensionDefinition()); + $container->register($id, 'stdClass')->setPublic(false)->addTag($tagName, $tagAttributes); $container->compile(); } @@ -210,11 +178,31 @@ class FormPassTest extends TestCase public function privateTaggedServicesProvider() { return array( - array('my.type', 'form.type', 'The service "my.type" must be public as form types are lazy-loaded'), - array('my.type_extension', 'form.type_extension', 'The service "my.type_extension" must be public as form type extensions are lazy-loaded'), - array('my.guesser', 'form.type_guesser', 'The service "my.guesser" must be public as form type guessers are lazy-loaded'), + array('my.type', 'form.type'), + array('my.type_extension', 'form.type_extension', array('extended_type' => 'Symfony\Component\Form\Extension\Core\Type\FormType')), + array('my.guesser', 'form.type_guesser'), ); } + + private function createContainerBuilder() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + return $container; + } + + private function createExtensionDefinition() + { + $definition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); + $definition->setArguments(array( + new ServiceLocatorArgument(array()), + array(), + new IteratorArgument(array()), + )); + + return $definition; + } } class FormPassTest_Type1 extends AbstractType diff --git a/src/Symfony/Component/Form/DependencyInjection/FormPass.php b/src/Symfony/Component/Form/DependencyInjection/FormPass.php index 19d7d22e44..5a18a69c3e 100644 --- a/src/Symfony/Component/Form/DependencyInjection/FormPass.php +++ b/src/Symfony/Component/Form/DependencyInjection/FormPass.php @@ -11,14 +11,18 @@ namespace Symfony\Component\Form\DependencyInjection; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; /** - * Adds all services with the tags "form.type" and "form.type_guesser" as - * arguments of the "form.extension" service. + * Adds all services with the tags "form.type", "form.type_extension" and + * "form.type_guesser" as arguments of the "form.extension" service. * * @author Bernhard Schussek */ @@ -46,29 +50,37 @@ class FormPass implements CompilerPassInterface } $definition = $container->getDefinition($this->formExtensionService); + $definition->replaceArgument(0, $this->processFormTypes($container, $definition)); + $definition->replaceArgument(1, $this->processFormTypeExtensions($container)); + $definition->replaceArgument(2, $this->processFormTypeGuessers($container)); + } - // Builds an array with fully-qualified type class names as keys and service IDs as values - $types = array(); - foreach ($container->findTaggedServiceIds($this->formTypeTag) as $serviceId => $tag) { - $serviceDefinition = $container->getDefinition($serviceId); - if (!$serviceDefinition->isPublic()) { - throw new InvalidArgumentException(sprintf('The service "%s" must be public as form types are lazy-loaded.', $serviceId)); - } - - // Support type access by FQCN - $types[$serviceDefinition->getClass()] = $serviceId; + private function processFormTypes(ContainerBuilder $container, Definition $definition) + { + // Get service locator argument + $servicesMap = array(); + $locator = $definition->getArgument(0); + if ($locator instanceof ServiceLocatorArgument) { + $servicesMap = $locator->getValues(); } - $definition->replaceArgument(1, $types); + // Builds an array with fully-qualified type class names as keys and service IDs as values + foreach ($container->findTaggedServiceIds($this->formTypeTag) as $serviceId => $tag) { + $serviceDefinition = $container->getDefinition($serviceId); + // Add form type service to the service locator + $servicesMap[$serviceDefinition->getClass()] = new Reference($serviceId); + } + + return new ServiceLocatorArgument($servicesMap); + } + + private function processFormTypeExtensions(ContainerBuilder $container) + { $typeExtensions = array(); - foreach ($this->findAndSortTaggedServices($this->formTypeExtensionTag, $container) as $reference) { $serviceId = (string) $reference; $serviceDefinition = $container->getDefinition($serviceId); - if (!$serviceDefinition->isPublic()) { - throw new InvalidArgumentException(sprintf('The service "%s" must be public as form type extensions are lazy-loaded.', $serviceId)); - } $tag = $serviceDefinition->getTag($this->formTypeExtensionTag); if (isset($tag[0]['extended_type'])) { @@ -77,19 +89,23 @@ class FormPass implements CompilerPassInterface 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)); } - $typeExtensions[$extendedType][] = $serviceId; + $typeExtensions[$extendedType][] = new Reference($serviceId); } - $definition->replaceArgument(2, $typeExtensions); - - $guessers = array_keys($container->findTaggedServiceIds($this->formTypeGuesserTag)); - foreach ($guessers as $serviceId) { - $serviceDefinition = $container->getDefinition($serviceId); - if (!$serviceDefinition->isPublic()) { - throw new InvalidArgumentException(sprintf('The service "%s" must be public as form type guessers are lazy-loaded.', $serviceId)); - } + foreach ($typeExtensions as $extendedType => $extensions) { + $typeExtensions[$extendedType] = new IteratorArgument($extensions); } - $definition->replaceArgument(3, $guessers); + return $typeExtensions; + } + + private function processFormTypeGuessers(ContainerBuilder $container) + { + $guessers = array(); + foreach ($container->findTaggedServiceIds($this->formTypeGuesserTag) as $serviceId => $tags) { + $guessers[] = new Reference($serviceId); + } + + return new IteratorArgument($guessers); } } diff --git a/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php b/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php index 8484c75520..048bf5f375 100644 --- a/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php +++ b/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php @@ -11,49 +11,80 @@ namespace Symfony\Component\Form\Extension\DependencyInjection; +use Psr\Container\ContainerInterface; use Symfony\Component\Form\FormExtensionInterface; use Symfony\Component\Form\FormTypeGuesserChain; use Symfony\Component\Form\Exception\InvalidArgumentException; -use Symfony\Component\DependencyInjection\ContainerInterface; class DependencyInjectionExtension implements FormExtensionInterface { - private $container; - private $typeServiceIds; - private $typeExtensionServiceIds; - private $guesserServiceIds; private $guesser; private $guesserLoaded = false; + private $typeContainer; + private $typeExtensionServices; + private $guesserServices; - public function __construct(ContainerInterface $container, array $typeServiceIds, array $typeExtensionServiceIds, array $guesserServiceIds) + // @deprecated to be removed in Symfony 4.0 + private $typeServiceIds; + private $guesserServiceIds; + + /** + * Constructor. + * + * @param ContainerInterface $typeContainer + * @param iterable[] $typeExtensionServices + * @param iterable $guesserServices + */ + public function __construct(ContainerInterface $typeContainer, array $typeExtensionServices, $guesserServices, array $guesserServiceIds = null) { - $this->container = $container; - $this->typeServiceIds = $typeServiceIds; - $this->typeExtensionServiceIds = $typeExtensionServiceIds; - $this->guesserServiceIds = $guesserServiceIds; + if (null !== $guesserServiceIds) { + @trigger_error(sprintf('Passing four arguments to the %s::__construct() method is deprecated since Symfony 3.3 and will be disallowed in Symfony 4.0. The new constructor only accepts three arguments.', __CLASS__), E_USER_DEPRECATED); + $this->guesserServiceIds = $guesserServiceIds; + $this->typeServiceIds = $typeExtensionServices; + } + + $this->typeContainer = $typeContainer; + $this->typeExtensionServices = $typeExtensionServices; + $this->guesserServices = $guesserServices; } public function getType($name) { - if (!isset($this->typeServiceIds[$name])) { - throw new InvalidArgumentException(sprintf('The field type "%s" is not registered with the service container.', $name)); + if (null !== $this->guesserServiceIds) { + if (!isset($this->typeServiceIds[$name])) { + throw new InvalidArgumentException(sprintf('The field type "%s" is not registered in the service container.', $name)); + } + + return $this->typeContainer->get($this->typeServiceIds[$name]); } - return $this->container->get($this->typeServiceIds[$name]); + if (!$this->typeContainer->has($name)) { + throw new InvalidArgumentException(sprintf('The field type "%s" is not registered in the service container.', $name)); + } + + return $this->typeContainer->get($name); } public function hasType($name) { - return isset($this->typeServiceIds[$name]); + if (null !== $this->guesserServiceIds) { + return isset($this->typeServiceIds[$name]); + } + + return $this->typeContainer->has($name); } public function getTypeExtensions($name) { $extensions = array(); - if (isset($this->typeExtensionServiceIds[$name])) { - foreach ($this->typeExtensionServiceIds[$name] as $serviceId) { - $extensions[] = $extension = $this->container->get($serviceId); + if (isset($this->typeExtensionServices[$name])) { + foreach ($this->typeExtensionServices[$name] as $serviceId => $extension) { + if (null !== $this->guesserServiceIds) { + $extension = $this->typeContainer->get($serviceId = $extension); + } + + $extensions[] = $extension; // validate result of getExtendedType() to ensure it is consistent with the service definition if ($extension->getExtendedType() !== $name) { @@ -73,7 +104,7 @@ class DependencyInjectionExtension implements FormExtensionInterface public function hasTypeExtensions($name) { - return isset($this->typeExtensionServiceIds[$name]); + return isset($this->typeExtensionServices[$name]); } public function getTypeGuesser() @@ -82,11 +113,15 @@ class DependencyInjectionExtension implements FormExtensionInterface $this->guesserLoaded = true; $guessers = array(); - foreach ($this->guesserServiceIds as $serviceId) { - $guessers[] = $this->container->get($serviceId); + foreach ($this->guesserServices as $serviceId => $service) { + if (null !== $this->guesserServiceIds) { + $service = $this->typeContainer->get($serviceId = $service); + } + + $guessers[] = $service; } - if (count($guessers) > 0) { + if ($guessers) { $this->guesser = new FormTypeGuesserChain($guessers); } } diff --git a/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php b/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php index 3858a0de35..799d4aff13 100644 --- a/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php +++ b/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Form\Tests\DependencyInjection; use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\Form\DependencyInjection\FormPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -25,8 +27,7 @@ class FormPassTest extends TestCase { public function testDoNothingIfFormExtensionNotLoaded() { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); + $container = $this->createContainerBuilder(); $container->compile(); @@ -35,18 +36,9 @@ class FormPassTest extends TestCase public function testAddTaggedTypes() { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); + $container = $this->createContainerBuilder(); - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); - $extDefinition->setArguments(array( - new Reference('service_container'), - array(), - array(), - array(), - )); - - $container->setDefinition('form.extension', $extDefinition); + $container->setDefinition('form.extension', $this->createExtensionDefinition()); $container->register('my.type1', __CLASS__.'_Type1')->addTag('form.type'); $container->register('my.type2', __CLASS__.'_Type2')->addTag('form.type'); @@ -54,10 +46,13 @@ class FormPassTest extends TestCase $extDefinition = $container->getDefinition('form.extension'); - $this->assertEquals(array( - __CLASS__.'_Type1' => 'my.type1', - __CLASS__.'_Type2' => 'my.type2', - ), $extDefinition->getArgument(1)); + $this->assertEquals( + new ServiceLocatorArgument(array( + __CLASS__.'_Type1' => new Reference('my.type1'), + __CLASS__.'_Type2' => new Reference('my.type2'), + )), + $extDefinition->getArgument(0) + ); } /** @@ -65,17 +60,9 @@ class FormPassTest extends TestCase */ public function testAddTaggedTypeExtensions(array $extensions, array $expectedRegisteredExtensions) { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); + $container = $this->createContainerBuilder(); - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array( - new Reference('service_container'), - array(), - array(), - array(), - )); - - $container->setDefinition('form.extension', $extDefinition); + $container->setDefinition('form.extension', $this->createExtensionDefinition()); foreach ($extensions as $serviceId => $tag) { $container->register($serviceId, 'stdClass')->addTag('form.type_extension', $tag); @@ -84,7 +71,7 @@ class FormPassTest extends TestCase $container->compile(); $extDefinition = $container->getDefinition('form.extension'); - $this->assertSame($expectedRegisteredExtensions, $extDefinition->getArgument(2)); + $this->assertEquals($expectedRegisteredExtensions, $extDefinition->getArgument(1)); } /** @@ -100,8 +87,11 @@ class FormPassTest extends TestCase 'my.type_extension3' => array('extended_type' => 'type2'), ), array( - 'type1' => array('my.type_extension1', 'my.type_extension2'), - 'type2' => array('my.type_extension3'), + 'type1' => new IteratorArgument(array( + new Reference('my.type_extension1'), + new Reference('my.type_extension2'), + )), + 'type2' => new IteratorArgument(array(new Reference('my.type_extension3'))), ), ), array( @@ -114,8 +104,16 @@ class FormPassTest extends TestCase 'my.type_extension6' => array('extended_type' => 'type2', 'priority' => 1), ), array( - 'type1' => array('my.type_extension2', 'my.type_extension1', 'my.type_extension3'), - 'type2' => array('my.type_extension4', 'my.type_extension5', 'my.type_extension6'), + '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'), + )), ), ), ); @@ -127,17 +125,9 @@ class FormPassTest extends TestCase */ public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttribute() { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); + $container = $this->createContainerBuilder(); - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array( - new Reference('service_container'), - array(), - array(), - array(), - )); - - $container->setDefinition('form.extension', $extDefinition); + $container->setDefinition('form.extension', $this->createExtensionDefinition()); $container->register('my.type_extension', 'stdClass') ->addTag('form.type_extension'); @@ -146,23 +136,14 @@ class FormPassTest extends TestCase public function testAddTaggedGuessers() { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); - - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); - $extDefinition->setArguments(array( - new Reference('service_container'), - array(), - array(), - array(), - )); + $container = $this->createContainerBuilder(); $definition1 = new Definition('stdClass'); $definition1->addTag('form.type_guesser'); $definition2 = new Definition('stdClass'); $definition2->addTag('form.type_guesser'); - $container->setDefinition('form.extension', $extDefinition); + $container->setDefinition('form.extension', $this->createExtensionDefinition()); $container->setDefinition('my.guesser1', $definition1); $container->setDefinition('my.guesser2', $definition2); @@ -170,49 +151,55 @@ class FormPassTest extends TestCase $extDefinition = $container->getDefinition('form.extension'); - $this->assertSame(array( - 'my.guesser1', - 'my.guesser2', - ), $extDefinition->getArgument(3)); + $this->assertEquals( + new IteratorArgument(array( + new Reference('my.guesser1'), + new Reference('my.guesser2'), + )), + $extDefinition->getArgument(2) + ); } /** * @dataProvider privateTaggedServicesProvider */ - public function testPrivateTaggedServices($id, $tagName, $expectedExceptionMessage) + public function testPrivateTaggedServices($id, $tagName, array $tagAttributes = array()) { - $container = new ContainerBuilder(); - $container->addCompilerPass(new FormPass()); - - $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); - $extDefinition->setArguments(array( - new Reference('service_container'), - array(), - array(), - array(), - )); - - $container->setDefinition('form.extension', $extDefinition); - $container->register($id, 'stdClass')->setPublic(false)->addTag($tagName); - - if (method_exists($this, 'expectException')) { - $this->expectException('InvalidArgumentException'); - $this->expectExceptionMessage($expectedExceptionMessage); - } else { - $this->setExpectedException('InvalidArgumentException', $expectedExceptionMessage); - } + $container = $this->createContainerBuilder(); + $container->setDefinition('form.extension', $this->createExtensionDefinition()); + $container->register($id, 'stdClass')->setPublic(false)->addTag($tagName, $tagAttributes); $container->compile(); } public function privateTaggedServicesProvider() { return array( - array('my.type', 'form.type', 'The service "my.type" must be public as form types are lazy-loaded'), - array('my.type_extension', 'form.type_extension', 'The service "my.type_extension" must be public as form type extensions are lazy-loaded'), - array('my.guesser', 'form.type_guesser', 'The service "my.guesser" must be public as form type guessers are lazy-loaded'), + array('my.type', 'form.type'), + array('my.type_extension', 'form.type_extension', array('extended_type' => 'Symfony\Component\Form\Extension\Core\Type\FormType')), + array('my.guesser', 'form.type_guesser'), ); } + + private function createExtensionDefinition() + { + $definition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); + $definition->setArguments(array( + new ServiceLocatorArgument(array()), + array(), + new IteratorArgument(array()), + )); + + return $definition; + } + + private function createContainerBuilder() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + return $container; + } } class FormPassTest_Type1 extends AbstractType diff --git a/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php index 0fb83f5dcf..4eb2354027 100644 --- a/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php @@ -19,25 +19,56 @@ class DependencyInjectionExtensionTest extends TestCase { public function testGetTypeExtensions() { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $container = $this->createContainerMock(); + $container->expects($this->never())->method('get'); - $typeExtension1 = $this->getMockBuilder('Symfony\Component\Form\FormTypeExtensionInterface')->getMock(); - $typeExtension1->expects($this->any()) - ->method('getExtendedType') - ->willReturn('test'); - $typeExtension2 = $this->getMockBuilder('Symfony\Component\Form\FormTypeExtensionInterface')->getMock(); - $typeExtension2->expects($this->any()) - ->method('getExtendedType') - ->willReturn('test'); - $typeExtension3 = $this->getMockBuilder('Symfony\Component\Form\FormTypeExtensionInterface')->getMock(); - $typeExtension3->expects($this->any()) - ->method('getExtendedType') - ->willReturn('other'); + $typeExtension1 = $this->createFormTypeExtensionMock('test'); + $typeExtension2 = $this->createFormTypeExtensionMock('test'); + $typeExtension3 = $this->createFormTypeExtensionMock('other'); + + $extensions = array( + 'test' => new \ArrayIterator(array($typeExtension1, $typeExtension2)), + 'other' => new \ArrayIterator(array($typeExtension3)), + ); + + $extension = new DependencyInjectionExtension($container, $extensions, array()); + + $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')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testThrowExceptionForInvalidExtendedType() + { + $container = $this->getMockBuilder('Psr\Container\ContainerInterface')->getMock(); + $container->expects($this->never())->method('get'); + + $extensions = array( + 'test' => new \ArrayIterator(array($this->createFormTypeExtensionMock('unmatched'))), + ); + + $extension = new DependencyInjectionExtension($container, $extensions, array()); + + $extension->getTypeExtensions('test'); + } + + /** + * @group legacy + * @expectedDeprecation Passing four arguments to the Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension::__construct() method is deprecated since Symfony 3.3 and will be disallowed in Symfony 4.0. The new constructor only accepts three arguments. + */ + public function testLegacyGetTypeExtensions() + { + $container = $this->createContainerMock(); $services = array( - 'extension1' => $typeExtension1, - 'extension2' => $typeExtension2, - 'extension3' => $typeExtension3, + 'extension1' => $typeExtension1 = $this->createFormTypeExtensionMock('test'), + 'extension2' => $typeExtension2 = $this->createFormTypeExtensionMock('test'), + 'extension3' => $typeExtension3 = $this->createFormTypeExtensionMock('other'), ); $container->expects($this->any()) @@ -50,7 +81,7 @@ class DependencyInjectionExtensionTest extends TestCase throw new ServiceNotFoundException($id); }); - $extension = new DependencyInjectionExtension($container, array(), array('test' => array('extension1', 'extension2'), 'other' => array('extension3')), array()); + $extension = new DependencyInjectionExtension($container, array('test' => array('extension1', 'extension2'), 'other' => array('extension3')), array(), array()); $this->assertTrue($extension->hasTypeExtensions('test')); $this->assertFalse($extension->hasTypeExtensions('unknown')); @@ -58,24 +89,36 @@ class DependencyInjectionExtensionTest extends TestCase } /** + * @group legacy * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + * @expectedDeprecation Passing four arguments to the Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension::__construct() method is deprecated since Symfony 3.3 and will be disallowed in Symfony 4.0. The new constructor only accepts three arguments. */ - public function testThrowExceptionForInvalidExtendedType() + public function testLegacyThrowExceptionForInvalidExtendedType() { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - - $typeExtension = $this->getMockBuilder('Symfony\Component\Form\FormTypeExtensionInterface')->getMock(); - $typeExtension->expects($this->any()) - ->method('getExtendedType') - ->willReturn('unmatched'); + $container = $this->createContainerMock(); $container->expects($this->any()) ->method('get') ->with('extension') - ->willReturn($typeExtension); + ->willReturn($this->createFormTypeExtensionMock('unmatched')); - $extension = new DependencyInjectionExtension($container, array(), array('test' => array('extension')), array()); + $extension = new DependencyInjectionExtension($container, array('test' => array('extension')), array(), array()); $extension->getTypeExtensions('test'); } + + private function createContainerMock() + { + return $this->getMockBuilder('Psr\Container\ContainerInterface') + ->setMethods(array('get', 'has')) + ->getMock(); + } + + private function createFormTypeExtensionMock($extendedType) + { + $extension = $this->getMockBuilder('Symfony\Component\Form\FormTypeExtensionInterface')->getMock(); + $extension->expects($this->any())->method('getExtendedType')->willReturn($extendedType); + + return $extension; + } } diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index 3f9a202973..39b6f66ab8 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -26,7 +26,7 @@ "require-dev": { "doctrine/collections": "~1.0", "symfony/validator": "^2.8.18|^3.2.5", - "symfony/dependency-injection": "~3.2", + "symfony/dependency-injection": "~3.3", "symfony/config": "~2.7|~3.0", "symfony/http-foundation": "~2.8|~3.0", "symfony/http-kernel": "~2.8|~3.0", @@ -36,7 +36,7 @@ }, "conflict": { "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", - "symfony/dependency-injection": "<3.2", + "symfony/dependency-injection": "<3.3", "symfony/doctrine-bridge": "<2.7", "symfony/framework-bundle": "<2.7", "symfony/twig-bridge": "<2.7",