[DI] Introducing autoconfigure: automatic _instanceof configuration

This commit is contained in:
Ryan Weaver 2017-03-31 06:45:36 -04:00 committed by Fabien Potencier
parent 4f0daa740a
commit 18627bf9f6
57 changed files with 410 additions and 25 deletions

View File

@ -222,6 +222,7 @@ class JsonDescriptor extends Descriptor
'shared' => $definition->isShared(),
'abstract' => $definition->isAbstract(),
'autowire' => $definition->isAutowired(),
'autoconfigure' => $definition->isAutoconfigured(),
);
foreach ($definition->getAutowiringTypes(false) as $autowiringType) {

View File

@ -183,6 +183,7 @@ class MarkdownDescriptor extends Descriptor
."\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) {

View File

@ -372,6 +372,7 @@ class XmlDescriptor extends Descriptor
$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();

View File

@ -13,30 +13,49 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;
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;
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 +244,45 @@ class FrameworkExtension extends Extension
'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(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)
->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',

View File

@ -11,6 +11,7 @@
"shared": true,
"abstract": true,
"autowire": false,
"autoconfigure": false,
"file": null,
"factory_class": "Full\\Qualified\\FactoryClass",
"factory_method": "get",

View File

@ -12,5 +12,6 @@
- Shared: yes
- Abstract: yes
- Autowired: no
- Autoconfigured: no
- Factory Class: `Full\Qualified\FactoryClass`
- Factory Method: `get`

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<alias id="alias_1" service="service_1" public="true"/>
<definition id="service_1" class="Full\Qualified\Class1" public="true" synthetic="false" lazy="true" shared="true" abstract="true" autowired="false" file="">
<definition id="service_1" class="Full\Qualified\Class1" public="true" synthetic="false" lazy="true" shared="true" abstract="true" autowired="false" autoconfigured="false" file="">
<factory class="Full\Qualified\FactoryClass" method="get"/>
</definition>

View File

@ -11,6 +11,7 @@
"shared": true,
"abstract": false,
"autowire": false,
"autoconfigure": false,
"file": "\/path\/to\/file",
"factory_service": "factory.service",
"factory_method": "get",

View File

@ -12,6 +12,7 @@
- Shared: yes
- Abstract: no
- Autowired: no
- Autoconfigured: no
- File: `/path/to/file`
- Factory Service: `factory.service`
- Factory Method: `get`

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<alias id="alias_2" service="service_2" public="false"/>
<definition id="service_2" class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" file="/path/to/file">
<definition id="service_2" class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="/path/to/file">
<factory service="factory.service" method="get"/>
<calls>
<call method="setMailer"/>

View File

@ -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": []

View File

@ -13,6 +13,7 @@ Definitions
- Shared: yes
- Abstract: yes
- Autowired: no
- Autoconfigured: no
- Arguments: yes
- Factory Class: `Full\Qualified\FactoryClass`
- Factory Method: `get`

View File

@ -2,12 +2,12 @@
<container>
<alias id="alias_1" service="service_1" public="true"/>
<alias id="alias_2" service="service_2" public="false"/>
<definition id="definition_1" class="Full\Qualified\Class1" public="true" synthetic="false" lazy="true" shared="true" abstract="true" autowired="false" file="">
<definition id="definition_1" class="Full\Qualified\Class1" public="true" synthetic="false" lazy="true" shared="true" abstract="true" autowired="false" autoconfigured="false" file="">
<factory class="Full\Qualified\FactoryClass" method="get"/>
<argument type="service" id="definition2"/>
<argument>%parameter%</argument>
<argument>
<definition class="inline_service" public="true" synthetic="false" lazy="false" shared="true" abstract="false" autowired="false" file="">
<definition class="inline_service" public="true" synthetic="false" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="">
<argument>arg1</argument>
<argument>arg2</argument>
</definition>
@ -16,7 +16,7 @@
<argument>foo</argument>
<argument type="service" id="definition2"/>
<argument>
<definition class="inline_service" public="true" synthetic="false" lazy="false" shared="true" abstract="false" autowired="false" file=""/>
<definition class="inline_service" public="true" synthetic="false" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file=""/>
</argument>
</argument>
<argument type="iterator">

View File

@ -8,6 +8,7 @@
"shared": true,
"abstract": true,
"autowire": false,
"autoconfigure": false,
"file": null,
"factory_class": "Full\\Qualified\\FactoryClass",
"factory_method": "get",

View File

@ -13,6 +13,7 @@ Definitions
- Shared: yes
- Abstract: yes
- Autowired: no
- Autoconfigured: no
- Factory Class: `Full\Qualified\FactoryClass`
- Factory Method: `get`

View File

@ -2,7 +2,7 @@
<container>
<alias id="alias_1" service="service_1" public="true"/>
<alias id="alias_2" service="service_2" public="false"/>
<definition id="definition_1" class="Full\Qualified\Class1" public="true" synthetic="false" lazy="true" shared="true" abstract="true" autowired="false" file="">
<definition id="definition_1" class="Full\Qualified\Class1" public="true" synthetic="false" lazy="true" shared="true" abstract="true" autowired="false" autoconfigured="false" file="">
<factory class="Full\Qualified\FactoryClass" method="get"/>
</definition>
<service id="service_container" class="Symfony\Component\DependencyInjection\ContainerBuilder"/>

View File

@ -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",

View File

@ -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`

View File

@ -2,10 +2,10 @@
<container>
<alias id="alias_1" service="service_1" public="true"/>
<alias id="alias_2" service="service_2" public="false"/>
<definition id="definition_1" class="Full\Qualified\Class1" public="true" synthetic="false" lazy="true" shared="true" abstract="true" autowired="false" file="">
<definition id="definition_1" class="Full\Qualified\Class1" public="true" synthetic="false" lazy="true" shared="true" abstract="true" autowired="false" autoconfigured="false" file="">
<factory class="Full\Qualified\FactoryClass" method="get"/>
</definition>
<definition id="definition_2" class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" file="/path/to/file">
<definition id="definition_2" class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="/path/to/file">
<factory service="factory.service" method="get"/>
<calls>
<call method="setMailer"/>

View File

@ -8,6 +8,7 @@
"shared": true,
"abstract": false,
"autowire": false,
"autoconfigure": false,
"file": "\/path\/to\/file",
"factory_service": "factory.service",
"factory_method": "get",

View File

@ -13,6 +13,7 @@ Definitions
- Shared: yes
- Abstract: no
- Autowired: no
- Autoconfigured: no
- File: `/path/to/file`
- Factory Service: `factory.service`
- Factory Method: `get`

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<container>
<definition id="definition_2" class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" file="/path/to/file">
<definition id="definition_2" class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="/path/to/file">
<factory service="factory.service" method="get"/>
<calls>
<call method="setMailer"/>

View File

@ -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",

View File

@ -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`

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<container>
<tag name="tag1">
<definition id="definition_2" class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" file="/path/to/file">
<definition id="definition_2" class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="/path/to/file">
<factory service="factory.service" method="get"/>
<calls>
<call method="setMailer"/>
@ -9,7 +9,7 @@
</definition>
</tag>
<tag name="tag2">
<definition id="definition_2" class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" file="/path/to/file">
<definition id="definition_2" class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="/path/to/file">
<factory service="factory.service" method="get"/>
<calls>
<call method="setMailer"/>

View File

@ -6,6 +6,7 @@
"shared": true,
"abstract": true,
"autowire": false,
"autoconfigure": false,
"file": null,
"factory_class": "Full\\Qualified\\FactoryClass",
"factory_method": "get",

View File

@ -5,5 +5,6 @@
- Shared: yes
- Abstract: yes
- Autowired: no
- Autoconfigured: no
- Factory Class: `Full\Qualified\FactoryClass`
- Factory Method: `get`

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<definition class="Full\Qualified\Class1" public="true" synthetic="false" lazy="true" shared="true" abstract="true" autowired="false" file="">
<definition class="Full\Qualified\Class1" public="true" synthetic="false" lazy="true" shared="true" abstract="true" autowired="false" autoconfigured="false" file="">
<factory class="Full\Qualified\FactoryClass" method="get"/>
</definition>

View File

@ -6,6 +6,7 @@
"shared": true,
"abstract": false,
"autowire": false,
"autoconfigure": false,
"file": "\/path\/to\/file",
"factory_service": "factory.service",
"factory_method": "get",

View File

@ -5,6 +5,7 @@
- Shared: yes
- Abstract: no
- Autowired: no
- Autoconfigured: no
- File: `/path/to/file`
- Factory Service: `factory.service`
- Factory Method: `get`

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<definition class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" file="/path/to/file">
<definition class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="/path/to/file">
<factory service="factory.service" method="get"/>
<calls>
<call method="setMailer"/>

View File

@ -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": []

View File

@ -5,6 +5,7 @@
- Shared: yes
- Abstract: yes
- Autowired: no
- Autoconfigured: no
- Arguments: yes
- Factory Class: `Full\Qualified\FactoryClass`
- Factory Method: `get`

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<definition class="Full\Qualified\Class1" public="true" synthetic="false" lazy="true" shared="true" abstract="true" autowired="false" file="">
<definition class="Full\Qualified\Class1" public="true" synthetic="false" lazy="true" shared="true" abstract="true" autowired="false" autoconfigured="false" file="">
<factory class="Full\Qualified\FactoryClass" method="get"/>
<argument type="service" id="definition2"/>
<argument>%parameter%</argument>
<argument>
<definition class="inline_service" public="true" synthetic="false" lazy="false" shared="true" abstract="false" autowired="false" file="">
<definition class="inline_service" public="true" synthetic="false" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="">
<argument>arg1</argument>
<argument>arg2</argument>
</definition>
@ -13,7 +13,7 @@
<argument>foo</argument>
<argument type="service" id="definition2"/>
<argument>
<definition class="inline_service" public="true" synthetic="false" lazy="false" shared="true" abstract="false" autowired="false" file=""/>
<definition class="inline_service" public="true" synthetic="false" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file=""/>
</argument>
</argument>
<argument type="iterator">

View File

@ -6,6 +6,7 @@
"shared": true,
"abstract": false,
"autowire": false,
"autoconfigure": false,
"arguments": [],
"file": "\/path\/to\/file",
"factory_service": "factory.service",

View File

@ -5,6 +5,7 @@
- Shared: yes
- Abstract: no
- Autowired: no
- Autoconfigured: no
- Arguments: no
- File: `/path/to/file`
- Factory Service: `factory.service`

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<definition class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" file="/path/to/file">
<definition class="Full\Qualified\Class2" public="false" synthetic="true" lazy="false" shared="true" abstract="false" autowired="false" autoconfigured="false" file="/path/to/file">
<factory service="factory.service" method="get"/>
<calls>
<call method="setMailer"/>

View File

@ -19,13 +19,13 @@ use Symfony\Component\DependencyInjection\Alias;
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 @@ class SecurityExtension extends Extension
$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(

View File

@ -13,6 +13,7 @@ namespace Symfony\Bundle\TwigBundle\DependencyInjection;
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 @@ class TwigExtension extends Extension
$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',

View File

@ -101,6 +101,7 @@ class ResolveDefinitionTemplatesPass extends AbstractRecursivePass
$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 @@ class ResolveDefinitionTemplatesPass extends AbstractRecursivePass
if (isset($changes['autowired'])) {
$def->setAutowired($definition->isAutowired());
}
if (isset($changes['autoconfigured'])) {
$def->setAutoconfigured($definition->isAutoconfigured());
}
if (isset($changes['shared'])) {
$def->setShared($definition->isShared());
}

View File

@ -38,25 +38,37 @@ class ResolveInstanceofConditionalsPass implements CompilerPassInterface
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 @@ class ResolveInstanceofConditionalsPass implements CompilerPassInterface
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;
}
}

View File

@ -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 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
$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 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
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.
*

View File

@ -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 @@ class Definition
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.
*

View File

@ -205,6 +205,10 @@ class XmlDumper extends Dumper
$service->appendChild($autowiringType);
}
if ($definition->isAutoconfigured()) {
$service->setAttribute('autoconfigure', 'true');
}
if ($callable = $definition->getConfigurator()) {
$configurator = $this->document->createElement('configurator');

View File

@ -180,6 +180,9 @@ class XmlFileLoader extends FileLoader
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 @@ class XmlFileLoader extends FileLoader
if (isset($defaults['autowire'])) {
$definition->setAutowired($defaults['autowire']);
}
if (isset($defaults['autoconfigure'])) {
$definition->setAutoconfigured($defaults['autoconfigure']);
}
$definition->setChanges(array());
}
@ -248,6 +254,10 @@ class XmlFileLoader extends FileLoader
$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);
}

View File

@ -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 @@ class YamlFileLoader extends FileLoader
if (isset($defaults['autowire'])) {
$definition->setAutowired($defaults['autowire']);
}
if (isset($defaults['autoconfigure'])) {
$definition->setAutoconfigured($defaults['autoconfigure']);
}
$definition->setChanges(array());
}
@ -510,6 +517,10 @@ class YamlFileLoader extends FileLoader
}
}
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));

View File

@ -104,6 +104,7 @@
<xsd:attribute name="public" type="boolean" />
<xsd:attribute name="autowire" type="boolean" />
<xsd:attribute name="inherit-tags" type="boolean" />
<xsd:attribute name="autoconfigure" type="boolean" />
</xsd:complexType>
<xsd:complexType name="service">
@ -132,6 +133,7 @@
<xsd:attribute name="decoration-priority" type="xsd:integer" />
<xsd:attribute name="autowire" type="boolean" />
<xsd:attribute name="inherit-tags" type="boolean" />
<xsd:attribute name="autoconfigure" type="boolean" />
</xsd:complexType>
<xsd:complexType name="instanceof">
@ -146,6 +148,7 @@
<xsd:attribute name="public" type="boolean" />
<xsd:attribute name="lazy" type="boolean" />
<xsd:attribute name="autowire" type="boolean" />
<xsd:attribute name="autoconfigure" type="boolean" />
</xsd:complexType>
<xsd:complexType name="prototype">
@ -167,6 +170,7 @@
<xsd:attribute name="parent" type="xsd:string" />
<xsd:attribute name="autowire" type="boolean" />
<xsd:attribute name="inherit-tags" type="boolean" />
<xsd:attribute name="autoconfigure" type="boolean" />
</xsd:complexType>
<xsd:complexType name="tag">

View File

@ -130,6 +130,7 @@ class IntegrationTest extends TestCase
// 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 @@ class IntegrationTest extends TestCase
// 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');

View File

@ -381,6 +381,38 @@ class ResolveDefinitionTemplatesPassTest extends TestCase
$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();

View File

@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase;
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 @@ class ResolveInstanceofConditionalsPassTest extends TestCase
(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 @@ class ResolveInstanceofConditionalsPassTest extends TestCase
$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());
}
}

View File

@ -567,6 +567,26 @@ class ContainerBuilderTest extends TestCase
$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 @@ class ContainerBuilderTest extends TestCase
$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

View File

@ -344,6 +344,7 @@ class DefinitionTest extends TestCase
$def->addTag('foo_tag');
$def->addMethodCall('methodCall');
$def->setProperty('fooprop', true);
$def->setAutoconfigured(true);
$this->assertSame(array(
'class' => true,
@ -356,6 +357,7 @@ class DefinitionTest extends TestCase
'lazy' => true,
'public' => true,
'shared' => true,
'autoconfigured' => true,
), $def->getChanges());
$def->setChanges(array());
@ -377,4 +379,12 @@ class DefinitionTest extends TestCase
$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());
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<defaults autoconfigure="true" />
<service id="use_defaults_settings" />
<service id="override_defaults_settings_to_false" autoconfigure="false" />
</services>
</container>

View File

@ -0,0 +1,9 @@
services:
_defaults:
autoconfigure: true
use_defaults_settings: ~
override_defaults_settings_to_false:
autoconfigure: false

View File

@ -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

View File

@ -693,6 +693,16 @@ class XmlFileLoaderTest extends TestCase
$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

View File

@ -567,6 +567,16 @@ class YamlFileLoaderTest extends TestCase
$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