From b389c643fdc6c9395a7e180ab2aba721570c807f Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 31 Mar 2017 06:45:36 -0400 Subject: [PATCH 1/3] [DI] Introducing a way for a Definition to opt into global instanceof-based configuration autoconfigure --- .../Console/Descriptor/JsonDescriptor.php | 1 + .../Console/Descriptor/MarkdownDescriptor.php | 1 + .../Console/Descriptor/XmlDescriptor.php | 1 + .../FrameworkExtension.php | 56 ++++++++++++++++++- .../Descriptor/alias_with_definition_1.json | 1 + .../Descriptor/alias_with_definition_1.md | 1 + .../Descriptor/alias_with_definition_1.xml | 2 +- .../Descriptor/alias_with_definition_2.json | 1 + .../Descriptor/alias_with_definition_2.md | 1 + .../Descriptor/alias_with_definition_2.xml | 2 +- .../Descriptor/builder_1_arguments.json | 3 + .../Descriptor/builder_1_arguments.md | 1 + .../Descriptor/builder_1_arguments.xml | 6 +- .../Fixtures/Descriptor/builder_1_public.json | 1 + .../Fixtures/Descriptor/builder_1_public.md | 1 + .../Fixtures/Descriptor/builder_1_public.xml | 2 +- .../Descriptor/builder_1_services.json | 2 + .../Fixtures/Descriptor/builder_1_services.md | 2 + .../Descriptor/builder_1_services.xml | 4 +- .../Fixtures/Descriptor/builder_1_tag1.json | 1 + .../Fixtures/Descriptor/builder_1_tag1.md | 1 + .../Fixtures/Descriptor/builder_1_tag1.xml | 2 +- .../Fixtures/Descriptor/builder_1_tags.json | 2 + .../Fixtures/Descriptor/builder_1_tags.md | 2 + .../Fixtures/Descriptor/builder_1_tags.xml | 4 +- .../Fixtures/Descriptor/definition_1.json | 1 + .../Tests/Fixtures/Descriptor/definition_1.md | 1 + .../Fixtures/Descriptor/definition_1.xml | 2 +- .../Fixtures/Descriptor/definition_2.json | 1 + .../Tests/Fixtures/Descriptor/definition_2.md | 1 + .../Fixtures/Descriptor/definition_2.xml | 2 +- .../Descriptor/definition_arguments_1.json | 3 + .../Descriptor/definition_arguments_1.md | 1 + .../Descriptor/definition_arguments_1.xml | 6 +- .../Descriptor/definition_arguments_2.json | 1 + .../Descriptor/definition_arguments_2.md | 1 + .../Descriptor/definition_arguments_2.xml | 2 +- .../DependencyInjection/SecurityExtension.php | 5 +- .../DependencyInjection/TwigExtension.php | 6 ++ .../ResolveDefinitionTemplatesPass.php | 4 ++ .../ResolveInstanceofConditionalsPass.php | 36 ++++++++++-- .../DependencyInjection/ContainerBuilder.php | 35 ++++++++++++ .../DependencyInjection/Definition.php | 25 +++++++++ .../DependencyInjection/Dumper/XmlDumper.php | 4 ++ .../Loader/XmlFileLoader.php | 10 ++++ .../Loader/YamlFileLoader.php | 11 ++++ .../schema/dic/services/services-1.0.xsd | 4 ++ .../Tests/Compiler/IntegrationTest.php | 2 + .../ResolveDefinitionTemplatesPassTest.php | 32 +++++++++++ .../ResolveInstanceofConditionalsPassTest.php | 50 ++++++++++++++++- .../Tests/ContainerBuilderTest.php | 31 ++++++++++ .../Tests/DefinitionTest.php | 10 ++++ .../Fixtures/xml/services_autoconfigure.xml | 9 +++ .../Fixtures/yaml/services_autoconfigure.yml | 9 +++ .../services_defaults_instanceof_parent.yml | 3 + .../Tests/Loader/XmlFileLoaderTest.php | 10 ++++ .../Tests/Loader/YamlFileLoaderTest.php | 10 ++++ 57 files changed, 404 insertions(+), 25 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_autoconfigure.xml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_autoconfigure.yml diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index 81d7233811f1b..c82f98675f3e1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -222,6 +222,7 @@ private function getContainerDefinitionData(Definition $definition, $omitTags = 'shared' => $definition->isShared(), 'abstract' => $definition->isAbstract(), 'autowire' => $definition->isAutowired(), + 'autoconfigure' => $definition->isAutoconfigured(), ); foreach ($definition->getAutowiringTypes(false) as $autowiringType) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php index c0319aad6bb19..2b10d3610b116 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -183,6 +183,7 @@ protected function describeContainerDefinition(Definition $definition, array $op ."\n".'- Shared: '.($definition->isShared() ? 'yes' : 'no') ."\n".'- Abstract: '.($definition->isAbstract() ? 'yes' : 'no') ."\n".'- Autowired: '.($definition->isAutowired() ? 'yes' : 'no') + ."\n".'- Autoconfigured: '.($definition->isAutoconfigured() ? 'yes' : 'no') ; foreach ($definition->getAutowiringTypes(false) as $autowiringType) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index 40e749aa6a3e7..f9e90e30ec1bb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -372,6 +372,7 @@ private function getContainerDefinitionDocument(Definition $definition, $id = nu $serviceXML->setAttribute('shared', $definition->isShared() ? 'true' : 'false'); $serviceXML->setAttribute('abstract', $definition->isAbstract() ? 'true' : 'false'); $serviceXML->setAttribute('autowired', $definition->isAutowired() ? 'true' : 'false'); + $serviceXML->setAttribute('autoconfigured', $definition->isAutoconfigured() ? 'true' : 'false'); $serviceXML->setAttribute('file', $definition->getFile()); $calls = $definition->getMethodCalls(); diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 9a757e8082aa6..acf506cba594a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -18,25 +18,42 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\Config\ResourceCheckerInterface; use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\LogicException; -use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Finder\Finder; +use Symfony\Component\Form\FormTypeGuesserInterface; +use Symfony\Component\Form\FormTypeInterface; +use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\PropertyAccess\PropertyAccessor; -use Symfony\Component\Serializer\Encoder\YamlEncoder; +use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\Serializer\Encoder\CsvEncoder; +use Symfony\Component\Serializer\Encoder\EncoderInterface; +use Symfony\Component\Serializer\Encoder\YamlEncoder; use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; use Symfony\Component\Serializer\Normalizer\DataUriNormalizer; use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Validator\ConstraintValidatorInterface; +use Symfony\Component\Validator\ObjectInitializerInterface; use Symfony\Component\WebLink\HttpHeaderSerializer; use Symfony\Component\Workflow; @@ -225,6 +242,41 @@ public function load(array $configs, ContainerBuilder $container) 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller', )); + $container->registerForAutoconfiguration(Command::class) + ->addTag('console.command'); + $container->registerForAutoconfiguration(ResourceCheckerInterface::class) + ->addTag('config_cache.resource_checker'); + $container->registerForAutoconfiguration(ServiceSubscriberInterface::class) + ->addTag('container.service_subscriber'); + $container->registerForAutoconfiguration(DataCollectorInterface::class) + ->addTag('data_collector'); + $container->registerForAutoconfiguration(FormTypeInterface::class) + ->addTag('form.type'); + $container->registerForAutoconfiguration(FormTypeGuesserInterface::class) + ->addTag('form.type_guesser'); + $container->registerForAutoconfiguration(CacheClearerInterface::class) + ->addTag('kernel.cache_clearer'); + $container->registerForAutoconfiguration(CacheWarmerInterface::class) + ->addTag('kernel.cache_warmer'); + $container->registerForAutoconfiguration(EventSubscriberInterface::class) + ->addTag('kernel.event_subscriber'); + $container->registerForAutoconfiguration(PropertyListExtractorInterface::class) + ->addTag('property_info.list_extractor'); + $container->registerForAutoconfiguration(PropertyTypeExtractorInterface::class) + ->addTag('property_info.type_extractor'); + $container->registerForAutoconfiguration(PropertyDescriptionExtractorInterface::class) + ->addTag('property_info.description_extractor'); + $container->registerForAutoconfiguration(PropertyAccessExtractorInterface::class) + ->addTag('property_info.access_extractor'); + $container->registerForAutoconfiguration(EncoderInterface::class) + ->addTag('serializer.encoder'); + $container->registerForAutoconfiguration(NormalizerInterface::class) + ->addTag('serializer.normalizer'); + $container->registerForAutoconfiguration(ConstraintValidatorInterface::class) + ->addTag('validator.constraint_validator'); + $container->registerForAutoconfiguration(ObjectInitializerInterface::class) + ->addTag('validator.initializer'); + if (PHP_VERSION_ID < 70000) { $this->addClassesToCompile(array( 'Symfony\\Component\\Config\\ConfigCache', diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.json index 09260d6159036..efcf34d06aa77 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.json @@ -11,6 +11,7 @@ "shared": true, "abstract": true, "autowire": false, + "autoconfigure": false, "file": null, "factory_class": "Full\\Qualified\\FactoryClass", "factory_method": "get", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.md index cb539aa795b16..ac56c0d4a4aff 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.md @@ -12,5 +12,6 @@ - Shared: yes - Abstract: yes - Autowired: no +- Autoconfigured: no - Factory Class: `Full\Qualified\FactoryClass` - Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.xml index 5515332b0875b..19f5a04344fef 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_1.xml @@ -1,5 +1,5 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.json index 03b4f36e13616..03780e3eebe7a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.json @@ -11,6 +11,7 @@ "shared": true, "abstract": false, "autowire": false, + "autoconfigure": false, "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.md index 6ebc5b8512f70..406c5dcada7a4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.md @@ -12,6 +12,7 @@ - Shared: yes - Abstract: no - Autowired: no +- Autoconfigured: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.xml index c549e50e59763..3c15460beebe8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_with_definition_2.xml @@ -1,6 +1,6 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.json index 7c43445239fb9..c304b33ac2283 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.json @@ -8,6 +8,7 @@ "shared": true, "abstract": true, "autowire": false, + "autoconfigure": false, "arguments": [ { "type": "service", @@ -22,6 +23,7 @@ "shared": true, "abstract": false, "autowire": false, + "autoconfigure": false, "arguments": [ "arg1", "arg2" @@ -43,6 +45,7 @@ "shared": true, "abstract": false, "autowire": false, + "autoconfigure": false, "arguments": [], "file": null, "tags": [] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.md index 1f1635e581494..29e34d2b0d94f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.md @@ -13,6 +13,7 @@ Definitions - Shared: yes - Abstract: yes - Autowired: no +- Autoconfigured: no - Arguments: yes - Factory Class: `Full\Qualified\FactoryClass` - Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml index f74ad13d7be74..2d4de06aa84cb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml @@ -2,12 +2,12 @@ - + %parameter% - + arg1 arg2 @@ -16,7 +16,7 @@ foo - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json index 43cf434d26f6b..7f86ccb2ac429 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json @@ -8,6 +8,7 @@ "shared": true, "abstract": true, "autowire": false, + "autoconfigure": false, "file": null, "factory_class": "Full\\Qualified\\FactoryClass", "factory_method": "get", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md index d21d18053677f..aff2f08191d00 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md @@ -13,6 +13,7 @@ Definitions - Shared: yes - Abstract: yes - Autowired: no +- Autoconfigured: no - Factory Class: `Full\Qualified\FactoryClass` - Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml index 52031e59aa4ac..22a7fb4224127 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml @@ -2,7 +2,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json index 794a10a1c74b1..becd607b797a5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json @@ -8,6 +8,7 @@ "shared": true, "abstract": true, "autowire": false, + "autoconfigure": false, "file": null, "factory_class": "Full\\Qualified\\FactoryClass", "factory_method": "get", @@ -21,6 +22,7 @@ "shared": true, "abstract": false, "autowire": false, + "autoconfigure": false, "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md index 26ea2f007ed51..54655668b435b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md @@ -13,6 +13,7 @@ Definitions - Shared: yes - Abstract: yes - Autowired: no +- Autoconfigured: no - Factory Class: `Full\Qualified\FactoryClass` - Factory Method: `get` @@ -25,6 +26,7 @@ Definitions - Shared: yes - Abstract: no - Autowired: no +- Autoconfigured: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml index bde934fa50a39..54da4f4f48427 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml @@ -2,10 +2,10 @@ - + - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json index 63f26d0a89237..fb9634f67a309 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json @@ -8,6 +8,7 @@ "shared": true, "abstract": false, "autowire": false, + "autoconfigure": false, "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md index e3dcc59f43bff..9895f1fb5d8b2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md @@ -13,6 +13,7 @@ Definitions - Shared: yes - Abstract: no - Autowired: no +- Autoconfigured: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml index d679db7b3b860..9c39b653dd5fc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml @@ -1,6 +1,6 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json index 42477a04228b6..7f49ac241ffde 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json @@ -8,6 +8,7 @@ "shared": true, "abstract": false, "autowire": false, + "autoconfigure": false, "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", @@ -25,6 +26,7 @@ "shared": true, "abstract": false, "autowire": false, + "autoconfigure": false, "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md index a0e1bcd1c4f8a..205596259d2c6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md @@ -13,6 +13,7 @@ tag1 - Shared: yes - Abstract: no - Autowired: no +- Autoconfigured: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` @@ -31,6 +32,7 @@ tag2 - Shared: yes - Abstract: no - Autowired: no +- Autoconfigured: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml index 08352fa206877..4e98e77a19a23 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml @@ -1,7 +1,7 @@ - + @@ -9,7 +9,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json index 2a24db63498fe..940bab12699b1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json @@ -6,6 +6,7 @@ "shared": true, "abstract": true, "autowire": false, + "autoconfigure": false, "file": null, "factory_class": "Full\\Qualified\\FactoryClass", "factory_method": "get", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md index b1d46b69f4f9f..e4d0429b9087d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md @@ -5,5 +5,6 @@ - Shared: yes - Abstract: yes - Autowired: no +- Autoconfigured: no - Factory Class: `Full\Qualified\FactoryClass` - Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml index 67d102945a6fe..d5015fdde3bd7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml @@ -1,4 +1,4 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json index fe83d858dc002..1c7b1ae14af42 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json @@ -6,6 +6,7 @@ "shared": true, "abstract": false, "autowire": false, + "autoconfigure": false, "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md index 1f097a2585321..6ff6d60b3dc93 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md @@ -5,6 +5,7 @@ - Shared: yes - Abstract: no - Autowired: no +- Autoconfigured: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml index 434a34b2565c4..72319eca97a4c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml @@ -1,5 +1,5 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.json index ad4ada52a9cd4..0590d3f76a611 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.json @@ -6,6 +6,7 @@ "shared": true, "abstract": true, "autowire": false, + "autoconfigure": false, "arguments": [ { "type": "service", @@ -20,6 +21,7 @@ "shared": true, "abstract": false, "autowire": false, + "autoconfigure": false, "arguments": [ "arg1", "arg2" @@ -41,6 +43,7 @@ "shared": true, "abstract": false, "autowire": false, + "autoconfigure": false, "arguments": [], "file": null, "tags": [] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.md index b4637aa023c0e..7cee17ad9cbb4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.md @@ -5,6 +5,7 @@ - Shared: yes - Abstract: yes - Autowired: no +- Autoconfigured: no - Arguments: yes - Factory Class: `Full\Qualified\FactoryClass` - Factory Method: `get` \ No newline at end of file diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml index 85935808c6b90..6d3cb8eea40be 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml @@ -1,10 +1,10 @@ - + %parameter% - + arg1 arg2 @@ -13,7 +13,7 @@ foo - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.json index 7385e98f11ea5..8f65d27b83ea9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.json @@ -6,6 +6,7 @@ "shared": true, "abstract": false, "autowire": false, + "autoconfigure": false, "arguments": [], "file": "\/path\/to\/file", "factory_service": "factory.service", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.md index 7ffe0e551af55..abe7dd475db2c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.md @@ -5,6 +5,7 @@ - Shared: yes - Abstract: no - Autowired: no +- Autoconfigured: no - Arguments: no - File: `/path/to/file` - Factory Service: `factory.service` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.xml index 434a34b2565c4..72319eca97a4c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_2.xml @@ -1,5 +1,5 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 520ccd6daf5f1..3099a7d359240 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -19,13 +19,13 @@ use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; -use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Config\FileLocator; use Symfony\Component\Security\Core\Authorization\ExpressionLanguage; +use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; /** * SecurityExtension. @@ -110,6 +110,9 @@ public function load(array $configs, ContainerBuilder $container) $this->aclLoad($config['acl'], $container); } + $container->registerForAutoconfiguration(VoterInterface::class) + ->addTag('security.voter'); + if (PHP_VERSION_ID < 70000) { // add some required classes for compilation $this->addClassesToCompile(array( diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index 790ab41a79df7..8e37cbba871b8 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -13,6 +13,7 @@ use Symfony\Bridge\Twig\Extension\WebLinkExtension; use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; @@ -143,6 +144,11 @@ public function load(array $configs, ContainerBuilder $container) $container->getDefinition('twig')->replaceArgument(1, $config); + $container->registerForAutoconfiguration(\Twig_ExtensionInterface::class) + ->addTag('twig.extension'); + $container->registerForAutoconfiguration(\Twig_LoaderInterface::class) + ->addTag('twig.loader'); + if (PHP_VERSION_ID < 70000) { $this->addClassesToCompile(array( 'Twig_Environment', diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php index 4e0b007c2a362..0be05dea23bcb 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php @@ -101,6 +101,7 @@ private function doResolveDefinition(ChildDefinition $definition) $def->setPublic($parentDef->isPublic()); $def->setLazy($parentDef->isLazy()); $def->setAutowired($parentDef->isAutowired()); + $def->setAutoconfigured($parentDef->isAutoconfigured()); $def->setChanges($parentDef->getChanges()); // overwrite with values specified in the decorator @@ -129,6 +130,9 @@ private function doResolveDefinition(ChildDefinition $definition) if (isset($changes['autowired'])) { $def->setAutowired($definition->isAutowired()); } + if (isset($changes['autoconfigured'])) { + $def->setAutoconfigured($definition->isAutoconfigured()); + } if (isset($changes['shared'])) { $def->setShared($definition->isShared()); } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php index 479fe12559639..b8800fe901454 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php @@ -38,25 +38,37 @@ public function process(ContainerBuilder $container) private function processDefinition(ContainerBuilder $container, $id, Definition $definition) { - if (!$instanceofConditionals = $definition->getInstanceofConditionals()) { + $instanceofConditionals = $definition->getInstanceofConditionals(); + $automaticInstanceofConditionals = $definition->isAutoconfigured() ? $container->getAutomaticInstanceofDefinitions() : array(); + + if (!$instanceofConditionals && !$automaticInstanceofConditionals) { return $definition; } + if (!$class = $container->getParameterBag()->resolveValue($definition->getClass())) { return $definition; } + $conditionals = $this->mergeConditionals($automaticInstanceofConditionals, $instanceofConditionals); + $definition->setInstanceofConditionals(array()); $parent = $shared = null; $instanceofTags = array(); - foreach ($instanceofConditionals as $interface => $instanceofDef) { + foreach ($conditionals as $interface => $instanceofDefs) { if ($interface !== $class && (!$container->getReflectionClass($interface) || !$container->getReflectionClass($class))) { continue; } - if ($interface === $class || is_subclass_of($class, $interface)) { + + if ($interface !== $class && !is_subclass_of($class, $interface)) { + continue; + } + + foreach ($instanceofDefs as $key => $instanceofDef) { + /** @var ChildDefinition $instanceofDef */ $instanceofDef = clone $instanceofDef; $instanceofDef->setAbstract(true)->setInheritTags(false)->setParent($parent ?: 'abstract.instanceof.'.$id); - $parent = 'instanceof.'.$interface.'.'.$id; + $parent = 'instanceof.'.$interface.'.'.$key.'.'.$id; $container->setDefinition($parent, $instanceofDef); $instanceofTags[] = $instanceofDef->getTags(); $instanceofDef->setTags(array()); @@ -100,4 +112,20 @@ private function processDefinition(ContainerBuilder $container, $id, Definition return $definition; } + + private function mergeConditionals(array $automaticInstanceofConditionals, array $instanceofConditionals) + { + // make each value an array of ChildDefinition + $conditionals = array_map(function($childDef) { return array($childDef); }, $automaticInstanceofConditionals); + + foreach ($instanceofConditionals as $interface => $instanceofDef) { + if (!isset($automaticInstanceofConditionals[$interface])) { + $conditionals[$interface] = array(); + } + + $conditionals[$interface][] = $instanceofDef; + } + + return $conditionals; + } } diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 86099458f807a..bab790ee2ab70 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -118,6 +118,8 @@ class ContainerBuilder extends Container implements TaggedContainerInterface */ private $vendors; + private $automaticInstanceofDefinitions = array(); + public function __construct(ParameterBagInterface $parameterBag = null) { parent::__construct($parameterBag); @@ -638,6 +640,14 @@ public function merge(ContainerBuilder $container) $this->envCounters[$env] += $count; } } + + foreach ($container->getAutomaticInstanceofDefinitions() as $interface => $childDefinition) { + if (isset($this->automaticInstanceofDefinitions[$interface])) { + throw new InvalidArgumentException(sprintf('%s has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface.', $interface)); + } + + $this->automaticInstanceofDefinitions[$interface] = $childDefinition; + } } /** @@ -1259,6 +1269,31 @@ public function getExpressionLanguageProviders() return $this->expressionLanguageProviders; } + /** + * Returns a ChildDefinition that will be used for autoconfiguring the interface/class. + * + * @param string $interface The class or interface to match + * @return ChildDefinition + */ + public function registerForAutoconfiguration($interface) + { + if (!isset($this->automaticInstanceofDefinitions[$interface])) { + $this->automaticInstanceofDefinitions[$interface] = new ChildDefinition(''); + } + + return $this->automaticInstanceofDefinitions[$interface]; + } + + /** + * Returns an array of ChildDefinition[] keyed by interface. + * + * @return ChildDefinition[] + */ + public function getAutomaticInstanceofDefinitions() + { + return $this->automaticInstanceofDefinitions; + } + /** * Resolves env parameter placeholders in a string or an array. * diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index af7bb5ee8072f..8599b968b769a 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -30,6 +30,7 @@ class Definition private $properties = array(); private $calls = array(); private $instanceof = array(); + private $autoconfigured = false; private $configurator; private $tags = array(); private $public = true; @@ -388,6 +389,30 @@ public function getInstanceofConditionals() return $this->instanceof; } + /** + * Sets whether or not instanceof conditionals should be prepended with a global set. + * + * @param bool $autoconfigured + * + * @return $this + */ + public function setAutoconfigured($autoconfigured) + { + $this->changes['autoconfigured'] = true; + + $this->autoconfigured = $autoconfigured; + + return $this; + } + + /** + * @return bool + */ + public function isAutoconfigured() + { + return $this->autoconfigured; + } + /** * Sets tags for this definition. * diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 302b3b081f280..c00c2167e2bab 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -205,6 +205,10 @@ private function addService($definition, $id, \DOMElement $parent) $service->appendChild($autowiringType); } + if ($definition->isAutoconfigured()) { + $service->setAttribute('autoconfigure', 'true'); + } + if ($callable = $definition->getConfigurator()) { $configurator = $this->document->createElement('configurator'); diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 8ea27b4e1677d..c31fc7fd502bd 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -180,6 +180,9 @@ private function getServiceDefaults(\DOMDocument $xml, $file) if ($defaultsNode->hasAttribute('inherit-tags')) { $defaults['inherit-tags'] = XmlUtils::phpize($defaultsNode->getAttribute('inherit-tags')); } + if ($defaultsNode->hasAttribute('autoconfigure')) { + $defaults['autoconfigure'] = XmlUtils::phpize($defaultsNode->getAttribute('autoconfigure')); + } return $defaults; } @@ -229,6 +232,9 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults = if (isset($defaults['autowire'])) { $definition->setAutowired($defaults['autowire']); } + if (isset($defaults['autoconfigure'])) { + $definition->setAutoconfigured($defaults['autoconfigure']); + } $definition->setChanges(array()); } @@ -248,6 +254,10 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults = $definition->setAutowired(XmlUtils::phpize($value)); } + if ($value = $service->getAttribute('autoconfigure')) { + $definition->setAutoconfigured(XmlUtils::phpize($value)); + } + if ($files = $this->getChildren($service, 'file')) { $definition->setFile($files[0]->nodeValue); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 52e91883eb3c8..735cb8244d0cc 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -57,6 +57,7 @@ class YamlFileLoader extends FileLoader 'decoration_priority' => 'decoration_priority', 'autowire' => 'autowire', 'autowiring_types' => 'autowiring_types', + 'autoconfigure' => 'autoconfigure', ); private static $prototypeKeywords = array( @@ -75,6 +76,7 @@ class YamlFileLoader extends FileLoader 'tags' => 'tags', 'inherit_tags' => 'inherit_tags', 'autowire' => 'autowire', + 'autoconfigure' => 'autoconfigure', ); private static $instanceofKeywords = array( @@ -86,6 +88,7 @@ class YamlFileLoader extends FileLoader 'calls' => 'calls', 'tags' => 'tags', 'autowire' => 'autowire', + 'autoconfigure' => 'autoconfigure', ); private static $defaultsKeywords = array( @@ -93,6 +96,7 @@ class YamlFileLoader extends FileLoader 'tags' => 'tags', 'inherit_tags' => 'inherit_tags', 'autowire' => 'autowire', + 'autoconfigure' => 'autoconfigure', ); private $yamlParser; @@ -369,6 +373,9 @@ private function parseDefinition($id, $service, $file, array $defaults) if (isset($defaults['autowire'])) { $definition->setAutowired($defaults['autowire']); } + if (isset($defaults['autoconfigure'])) { + $definition->setAutoconfigured($defaults['autoconfigure']); + } $definition->setChanges(array()); } @@ -510,6 +517,10 @@ private function parseDefinition($id, $service, $file, array $defaults) } } + if (isset($service['autoconfigure'])) { + $definition->setAutoconfigured($service['autoconfigure']); + } + if (array_key_exists('resource', $service)) { if (!is_string($service['resource'])) { throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file)); diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index 79a9ad4637cf6..c659694ffaa39 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -104,6 +104,7 @@ + @@ -132,6 +133,7 @@ + @@ -146,6 +148,7 @@ + @@ -167,6 +170,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php index 962020700bdf0..9d75c0b962186 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php @@ -130,6 +130,7 @@ public function testInstanceofDefaultsAndParentDefinitionResolution() // instanceof overrides defaults $simpleService = $container->getDefinition('service_simple'); $this->assertFalse($simpleService->isAutowired()); + $this->assertFalse($simpleService->isAutoconfigured()); $this->assertFalse($simpleService->isShared()); // all tags are kept @@ -156,6 +157,7 @@ public function testInstanceofDefaultsAndParentDefinitionResolution() // service override instanceof $overrideService = $container->getDefinition('service_override_instanceof'); $this->assertTrue($overrideService->isAutowired()); + $this->assertTrue($overrideService->isAutoconfigured()); // children definitions get no instanceof $childDef = $container->getDefinition('child_service'); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php index 4a21f4a53b28c..a2dd9b130d536 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php @@ -381,6 +381,38 @@ public function testProcessSetsArguments() $this->assertSame(array(2, 1, 'foo' => 3), $def->getArguments()); } + public function testSetAutoconfiguredOnServiceHasParent() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'stdClass') + ->setAutoconfigured(true) + ; + + $container->setDefinition('child1', new ChildDefinition('parent')) + ->setAutoconfigured(false) + ; + + $this->process($container); + + $this->assertFalse($container->getDefinition('child1')->isAutoconfigured()); + } + + public function testSetAutoconfiguredOnServiceIsParent() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'stdClass') + ->setAutoconfigured(true) + ; + + $container->setDefinition('child1', new ChildDefinition('parent')); + + $this->process($container); + + $this->assertTrue($container->getDefinition('child1')->isAutoconfigured()); + } + protected function process(ContainerBuilder $container) { $pass = new ResolveDefinitionTemplatesPass(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php index 0829aa8b3efd1..06ae31f08504c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass; +use Symfony\Component\DependencyInjection\Compiler\ResolveTagsInheritancePass; use Symfony\Component\DependencyInjection\ContainerBuilder; class ResolveInstanceofConditionalsPassTest extends TestCase @@ -29,7 +30,7 @@ public function testProcess() (new ResolveInstanceofConditionalsPass())->process($container); - $parent = 'instanceof.'.parent::class.'.foo'; + $parent = 'instanceof.'.parent::class.'.0.foo'; $def = $container->getDefinition('foo'); $this->assertEmpty($def->getInstanceofConditionals()); $this->assertInstanceof(ChildDefinition::class, $def); @@ -106,4 +107,51 @@ public function testProcessHandlesMultipleInheritance() $this->assertTrue($def->isLazy()); $this->assertTrue($def->isShared()); } + + public function testProcessUsesAutomaticInstanceofDefinitions() + { + $container = new ContainerBuilder(); + $def = $container->register('normal_service', self::class); + $def->setInstanceofConditionals(array( + parent::class => (new ChildDefinition('')) + ->addTag('local_instanceof_tag') + ->setFactory('locally_set_factory'), + )); + $def->setAutoconfigured(true); + $container->registerForAutoconfiguration(parent::class) + ->addTag('automatic_instanceof_tag') + ->setAutowired(true) + ->setFactory('automatically_set_factory'); + + (new ResolveInstanceofConditionalsPass())->process($container); + (new ResolveTagsInheritancePass())->process($container); + (new ResolveDefinitionTemplatesPass())->process($container); + + $def = $container->getDefinition('normal_service'); + // autowired thanks to the automatic instanceof + $this->assertTrue($def->isAutowired()); + // factory from the specific instanceof overrides global one + $this->assertEquals('locally_set_factory', $def->getFactory()); + // tags are merged, the locally set one is first + $this->assertSame(array('local_instanceof_tag' => array(array()), 'automatic_instanceof_tag' => array(array())), $def->getTags()); + } + + public function testProcessDoesNotUseAutomaticInstanceofDefinitionsIfNotEnabled() + { + $container = new ContainerBuilder(); + $def = $container->register('normal_service', self::class); + $def->setInstanceofConditionals(array( + parent::class => (new ChildDefinition('')) + ->addTag('foo_tag'), + )); + $container->registerForAutoconfiguration(parent::class) + ->setAutowired(true); + + (new ResolveInstanceofConditionalsPass())->process($container); + (new ResolveDefinitionTemplatesPass())->process($container); + + $def = $container->getDefinition('normal_service'); + // no automatic_tag, it was not enabled on the Definition + $this->assertFalse($def->isAutowired()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 5580d3c3a4f1a..94d99db9dcff1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -567,6 +567,26 @@ public function testMerge() $this->assertSame(array('%env(Bar)%'), $config->resolveEnvPlaceholders(array($bag->get('env(Bar)')))); $container->merge($config); $this->assertEquals(array('Foo' => 0, 'Bar' => 1), $container->getEnvCounters()); + + $container = new ContainerBuilder(); + $config = new ContainerBuilder(); + $childDefA = $container->registerForAutoconfiguration('AInterface'); + $childDefB = $config->registerForAutoconfiguration('BInterface'); + $container->merge($config); + $this->assertSame(array('AInterface' => $childDefA, 'BInterface' => $childDefB), $container->getAutomaticInstanceofDefinitions()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage AInterface has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface. + */ + public function testMergeThrowsExceptionForDuplicateAutomaticInstanceofDefinitions() + { + $container = new ContainerBuilder(); + $config = new ContainerBuilder(); + $container->registerForAutoconfiguration('AInterface'); + $config->registerForAutoconfiguration('AInterface'); + $container->merge($config); } public function testResolveEnvValues() @@ -1097,6 +1117,17 @@ public function testServiceLocator() $this->assertInstanceOf(ServiceLocator::class, $foo = $container->get('foo_service')); $this->assertSame($container->get('bar_service'), $foo->get('bar')); } + + public function testRegisterForAutoconfiguration() + { + $container = new ContainerBuilder(); + $childDefA = $container->registerForAutoconfiguration('AInterface'); + $childDefB = $container->registerForAutoconfiguration('BInterface'); + $this->assertSame(array('AInterface' => $childDefA, 'BInterface' => $childDefB), $container->getAutomaticInstanceofDefinitions()); + + // when called multiple times, the same instance is returned + $this->assertSame($childDefA, $container->registerForAutoconfiguration('AInterface')); + } } class FooClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php index 797c8cc0feb13..2821dc17c0dc2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -344,6 +344,7 @@ public function testGetChangesWithChanges() $def->addTag('foo_tag'); $def->addMethodCall('methodCall'); $def->setProperty('fooprop', true); + $def->setAutoconfigured(true); $this->assertSame(array( 'class' => true, @@ -356,6 +357,7 @@ public function testGetChangesWithChanges() 'lazy' => true, 'public' => true, 'shared' => true, + 'autoconfigured' => true, ), $def->getChanges()); $def->setChanges(array()); @@ -377,4 +379,12 @@ public function testTypes() $this->assertSame($def, $def->removeAutowiringType('Foo')); $this->assertEquals(array('Bar'), $def->getAutowiringTypes()); } + + public function testShouldAutoconfigure() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isAutoconfigured()); + $def->setAutoconfigured(true); + $this->assertTrue($def->isAutoconfigured()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_autoconfigure.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_autoconfigure.xml new file mode 100644 index 0000000000000..5e855c097288e --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_autoconfigure.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_autoconfigure.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_autoconfigure.yml new file mode 100644 index 0000000000000..809c9f47ddfda --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_autoconfigure.yml @@ -0,0 +1,9 @@ + +services: + _defaults: + autoconfigure: true + + use_defaults_settings: ~ + + override_defaults_settings_to_false: + autoconfigure: false diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_instanceof_parent.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_instanceof_parent.yml index 838e0626e7dbd..9e332a8405b0b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_instanceof_parent.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_instanceof_parent.yml @@ -1,11 +1,13 @@ services: _defaults: autowire: true + autoconfigure: true _instanceof: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStubParent: # should override _defaults autowire: false + autoconfigure: false shared: false tags: - { name: foo_tag, tag_option: from_instanceof } @@ -30,6 +32,7 @@ services: class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub # override instanceof autowire: true + autoconfigure: true parent_service: abstract: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 8b628b098798a..5d9732119515b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -693,6 +693,16 @@ public function testInstanceof() $this->assertTrue($definition->isLazy()); $this->assertSame(array('foo' => array(array()), 'bar' => array(array())), $definition->getTags()); } + + public function testAutoConfigureInstanceof() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_autoconfigure.xml'); + + $this->assertTrue($container->getDefinition('use_defaults_settings')->isAutoconfigured()); + $this->assertFalse($container->getDefinition('override_defaults_settings_to_false')->isAutoconfigured()); + } } interface BarInterface diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 8f628ae3e22cb..c0cf1e82cf78e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -567,6 +567,16 @@ public function testAnonymousServicesInParameters() $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); $loader->load('anonymous_services_in_parameters.yml'); } + + public function testAutoConfigureInstanceof() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_autoconfigure.yml'); + + $this->assertTrue($container->getDefinition('use_defaults_settings')->isAutoconfigured()); + $this->assertFalse($container->getDefinition('override_defaults_settings_to_false')->isAutoconfigured()); + } } interface FooInterface From 8caad4161dd29d482d20768536f1febb137846bc Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 16 Apr 2017 07:20:47 -0400 Subject: [PATCH 2/3] Adding controller.service_arguments to AbstractController instances --- .../FrameworkBundle/DependencyInjection/FrameworkExtension.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index acf506cba594a..e1c35dc0f849e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -13,6 +13,7 @@ use Doctrine\Common\Annotations\Reader; use Symfony\Bridge\Monolog\Processor\DebugProcessor; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Config\FileLocator; @@ -248,6 +249,8 @@ public function load(array $configs, ContainerBuilder $container) ->addTag('config_cache.resource_checker'); $container->registerForAutoconfiguration(ServiceSubscriberInterface::class) ->addTag('container.service_subscriber'); + $container->registerForAutoconfiguration(AbstractController::class) + ->addTag('controller.service_arguments'); $container->registerForAutoconfiguration(DataCollectorInterface::class) ->addTag('data_collector'); $container->registerForAutoconfiguration(FormTypeInterface::class) From 5b366810c1a93b2d2d556b0cf20513a4ec32da59 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 17 Apr 2017 06:13:10 -0400 Subject: [PATCH 3/3] Tagging classes extending Controller with controller.service_arguments It is also valid to use these classes like a service --- .../FrameworkBundle/DependencyInjection/FrameworkExtension.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index e1c35dc0f849e..666b4cc31088d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -14,6 +14,7 @@ use Doctrine\Common\Annotations\Reader; use Symfony\Bridge\Monolog\Processor\DebugProcessor; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Config\FileLocator; @@ -251,6 +252,8 @@ public function load(array $configs, ContainerBuilder $container) ->addTag('container.service_subscriber'); $container->registerForAutoconfiguration(AbstractController::class) ->addTag('controller.service_arguments'); + $container->registerForAutoconfiguration(Controller::class) + ->addTag('controller.service_arguments'); $container->registerForAutoconfiguration(DataCollectorInterface::class) ->addTag('data_collector'); $container->registerForAutoconfiguration(FormTypeInterface::class)