feature #15613 [DependencyInjection] Add autowiring capabilities (dunglas)
This PR was squashed before being merged into the 2.8 branch (closes #15613).
Discussion
----------
[DependencyInjection] Add autowiring capabilities
| Q | A
| ------------- | ---
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | n/a
| License | MIT
| Doc PR | not yet
This PR adds autowiring capabilities to the Dependency Injection component. It eases service registration by letting the component guessing dependencies to inject and even (under certain conditions) registering them using typehints of the constructor parameters.
The following usages are supported:
# Automatic dependency registration
```php
class Foo
{
}
class Bar
{
public function __construct(Foo $f)
{
}
}
```
```yaml
services:
bar:
class: Bar
autowire: true
```
It will register `Foo` as a private service (`autowired.foo`) and injects it as the first argument of the `bar` constructor.
This method works only for typehints corresponding to instantiable classes (interfaces and abstract classes are not supported).
# Autocompletion of definition arguments
```php
interface A
{
}
interface B extends A
{
}
class Foo implements B
{
}
class Bar
{
}
class Baz extends Bar
{
}
class LesTilleuls
{
public function __construct(A $a, Bar $bar)
{
}
}
```
```yaml
services:
foo:
class: Foo
baz:
class: Baz
les_tilleuls:
class: LesTilleuls
autowire: true
```
The autowiring system will find types of all services and completes constructor arguments of the `les_tilleuls` service definition using typehints.
It works only if there is one service registered for a given type (if there are several services available for the same type and no explicit type definition, a `RuntimeException` is thrown).
# Explicit type definition
```php
interface A
{
}
class A1 implements A
{
}
class A2 implements A
{
}
class B
{
public function __construct(A $a)
{
}
}
```
```yaml
services:
a1:
class: A1
types: [ A ]
a2:
class: A2
# Will be autowired with A1
class b:
class: B
autowire: true
# Not autowired
class another_b:
class: B
arguments: [ @a2 ]
autowire: true
```
When a service is explicitly associated with a type, it is always used to fill a definition depending of this type, even if several services have this type. If several services are associated with the same type, the last definition takes the priority.
Of course explicit definitions are still supported.
YAML, XML and PHP loaders have been updated to supports the new `type` parameter.
Commits
-------
aee5731
[DependencyInjection] Add autowiring capabilities
This commit is contained in:
commit
478ad543b7
@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Guesses constructor arguments of services definitions and try to instantiate services if necessary.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class AutowirePass implements CompilerPassInterface
|
||||
{
|
||||
private $container;
|
||||
private $reflectionClasses = array();
|
||||
private $definedTypes = array();
|
||||
private $types;
|
||||
private $notGuessableTypes = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
foreach ($container->getDefinitions() as $id => $definition) {
|
||||
if ($definition->isAutowired()) {
|
||||
$this->completeDefinition($id, $definition);
|
||||
}
|
||||
}
|
||||
|
||||
// Free memory and remove circular reference to container
|
||||
$this->container = null;
|
||||
$this->reflectionClasses = array();
|
||||
$this->definedTypes = array();
|
||||
$this->types = null;
|
||||
$this->notGuessableTypes = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wires the given definition.
|
||||
*
|
||||
* @param string $id
|
||||
* @param Definition $definition
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function completeDefinition($id, Definition $definition)
|
||||
{
|
||||
if (!$reflectionClass = $this->getReflectionClass($id, $definition)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->container->addClassResource($reflectionClass);
|
||||
|
||||
if (!$constructor = $reflectionClass->getConstructor()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$arguments = $definition->getArguments();
|
||||
foreach ($constructor->getParameters() as $index => $parameter) {
|
||||
$argumentExists = array_key_exists($index, $arguments);
|
||||
if ($argumentExists && '' !== $arguments[$index]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!$typeHint = $parameter->getClass()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (null === $this->types) {
|
||||
$this->populateAvailableTypes();
|
||||
}
|
||||
|
||||
if (isset($this->types[$typeHint->name])) {
|
||||
$value = new Reference($this->types[$typeHint->name]);
|
||||
} else {
|
||||
try {
|
||||
$value = $this->createAutowiredDefinition($typeHint, $id);
|
||||
} catch (RuntimeException $e) {
|
||||
if (!$parameter->isDefaultValueAvailable()) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$value = $parameter->getDefaultValue();
|
||||
}
|
||||
}
|
||||
} catch (\ReflectionException $reflectionException) {
|
||||
// Typehint against a non-existing class
|
||||
|
||||
if (!$parameter->isDefaultValueAvailable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $parameter->getDefaultValue();
|
||||
}
|
||||
|
||||
if ($argumentExists) {
|
||||
$definition->replaceArgument($index, $value);
|
||||
} else {
|
||||
$definition->addArgument($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the list of available types.
|
||||
*/
|
||||
private function populateAvailableTypes()
|
||||
{
|
||||
$this->types = array();
|
||||
|
||||
foreach ($this->container->getDefinitions() as $id => $definition) {
|
||||
$this->populateAvailableType($id, $definition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the list of available types for a given definition.
|
||||
*
|
||||
* @param string $id
|
||||
* @param Definition $definition
|
||||
*/
|
||||
private function populateAvailableType($id, Definition $definition)
|
||||
{
|
||||
if (!$definition->getClass()) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($definition->getAutowiringTypes() as $type) {
|
||||
$this->definedTypes[$type] = true;
|
||||
$this->types[$type] = $id;
|
||||
}
|
||||
|
||||
if ($reflectionClass = $this->getReflectionClass($id, $definition)) {
|
||||
$this->extractInterfaces($id, $reflectionClass);
|
||||
$this->extractAncestors($id, $reflectionClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the list of all interfaces implemented by a class.
|
||||
*
|
||||
* @param string $id
|
||||
* @param \ReflectionClass $reflectionClass
|
||||
*/
|
||||
private function extractInterfaces($id, \ReflectionClass $reflectionClass)
|
||||
{
|
||||
foreach ($reflectionClass->getInterfaces() as $interfaceName => $reflectionInterface) {
|
||||
$this->set($interfaceName, $id);
|
||||
|
||||
$this->extractInterfaces($id, $reflectionInterface);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts all inherited types of a class.
|
||||
*
|
||||
* @param string $id
|
||||
* @param \ReflectionClass $reflectionClass
|
||||
*/
|
||||
private function extractAncestors($id, \ReflectionClass $reflectionClass)
|
||||
{
|
||||
$this->set($reflectionClass->name, $id);
|
||||
|
||||
if ($reflectionParentClass = $reflectionClass->getParentClass()) {
|
||||
$this->extractAncestors($id, $reflectionParentClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates a type and a service id if applicable.
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $id
|
||||
*/
|
||||
private function set($type, $id)
|
||||
{
|
||||
if (isset($this->definedTypes[$type]) || isset($this->notGuessableTypes[$type])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($this->types[$type])) {
|
||||
if ($this->types[$type] === $id) {
|
||||
return;
|
||||
}
|
||||
|
||||
unset($this->types[$type]);
|
||||
$this->notGuessableTypes[$type] = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->types[$type] = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a definition for the type if possible or throws an exception.
|
||||
*
|
||||
* @param \ReflectionClass $typeHint
|
||||
* @param string $id
|
||||
*
|
||||
* @return Reference A reference to the registered definition
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function createAutowiredDefinition(\ReflectionClass $typeHint, $id)
|
||||
{
|
||||
if (!$typeHint->isInstantiable()) {
|
||||
throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s".', $typeHint->name, $id));
|
||||
}
|
||||
|
||||
$argumentId = sprintf('autowired.%s', $typeHint->name);
|
||||
|
||||
$argumentDefinition = $this->container->register($argumentId, $typeHint->name);
|
||||
$argumentDefinition->setPublic(false);
|
||||
|
||||
$this->populateAvailableType($argumentId, $argumentDefinition);
|
||||
$this->completeDefinition($argumentId, $argumentDefinition);
|
||||
|
||||
return new Reference($argumentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the reflection class associated with the given service.
|
||||
*
|
||||
* @param string $id
|
||||
* @param Definition $definition
|
||||
*
|
||||
* @return \ReflectionClass|null
|
||||
*/
|
||||
private function getReflectionClass($id, Definition $definition)
|
||||
{
|
||||
if (isset($this->reflectionClasses[$id])) {
|
||||
return $this->reflectionClasses[$id];
|
||||
}
|
||||
|
||||
if (!$class = $definition->getClass()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$class = $this->container->getParameterBag()->resolveValue($class);
|
||||
|
||||
try {
|
||||
return $this->reflectionClasses[$id] = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException $reflectionException) {
|
||||
// return null
|
||||
}
|
||||
}
|
||||
}
|
@ -50,6 +50,7 @@ class PassConfig
|
||||
new CheckDefinitionValidityPass(),
|
||||
new ResolveReferencesToAliasesPass(),
|
||||
new ResolveInvalidReferencesPass(),
|
||||
new AutowirePass(),
|
||||
new AnalyzeServiceReferencesPass(true),
|
||||
new CheckCircularReferencesPass(),
|
||||
new CheckReferenceValidityPass(),
|
||||
|
@ -41,6 +41,8 @@ class Definition
|
||||
private $synchronized = false;
|
||||
private $lazy = false;
|
||||
private $decoratedService;
|
||||
private $autowired = false;
|
||||
private $autowiringTypes = array();
|
||||
|
||||
protected $arguments;
|
||||
|
||||
@ -818,4 +820,96 @@ class Definition
|
||||
{
|
||||
return $this->configurator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets types that will default to this definition.
|
||||
*
|
||||
* @param string[] $types
|
||||
*
|
||||
* @return Definition The current instance
|
||||
*/
|
||||
public function setAutowiringTypes(array $types)
|
||||
{
|
||||
$this->autowiringTypes = array();
|
||||
|
||||
foreach ($types as $type) {
|
||||
$this->autowiringTypes[$type] = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the definition autowired?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAutowired()
|
||||
{
|
||||
return $this->autowired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets autowired.
|
||||
*
|
||||
* @param $autowired
|
||||
*
|
||||
* @return Definition The current instance
|
||||
*/
|
||||
public function setAutowired($autowired)
|
||||
{
|
||||
$this->autowired = $autowired;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets autowiring types that will default to this definition.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAutowiringTypes()
|
||||
{
|
||||
return array_keys($this->autowiringTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a type that will default to this definition.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return Definition The current instance
|
||||
*/
|
||||
public function addAutowiringType($type)
|
||||
{
|
||||
$this->autowiringTypes[$type] = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a type.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return Definition The current instance
|
||||
*/
|
||||
public function removeAutowiringType($type)
|
||||
{
|
||||
unset($this->autowiringTypes[$type]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will this definition default for the given type?
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAutowiringType($type)
|
||||
{
|
||||
return isset($this->autowiringTypes[$type]);
|
||||
}
|
||||
}
|
||||
|
@ -157,6 +157,10 @@ class XmlFileLoader extends FileLoader
|
||||
}
|
||||
}
|
||||
|
||||
if ($value = $service->getAttribute('autowire')) {
|
||||
$definition->setAutowired(XmlUtils::phpize($value));
|
||||
}
|
||||
|
||||
if ($value = $service->getAttribute('scope')) {
|
||||
$triggerDeprecation = 'request' !== (string) $service->getAttribute('id');
|
||||
|
||||
@ -247,6 +251,10 @@ class XmlFileLoader extends FileLoader
|
||||
$definition->addTag($tag->getAttribute('name'), $parameters);
|
||||
}
|
||||
|
||||
foreach ($this->getChildren($service, 'autowiring-type') as $type) {
|
||||
$definition->addAutowiringType($type->textContent);
|
||||
}
|
||||
|
||||
if ($value = $service->getAttribute('decorates')) {
|
||||
$renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null;
|
||||
$priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0;
|
||||
|
@ -299,6 +299,28 @@ class YamlFileLoader extends FileLoader
|
||||
$definition->setDecoratedService($service['decorates'], $renameId, $priority);
|
||||
}
|
||||
|
||||
if (isset($service['autowire'])) {
|
||||
$definition->setAutowired($service['autowire']);
|
||||
}
|
||||
|
||||
if (isset($service['autowiring_types'])) {
|
||||
if (is_string($service['autowiring_types'])) {
|
||||
$definition->addAutowiringType($service['autowiring_types']);
|
||||
} else {
|
||||
if (!is_array($service['autowiring_types'])) {
|
||||
throw new InvalidArgumentException(sprintf('Parameter "autowiring_types" must be a string or an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
|
||||
}
|
||||
|
||||
foreach ($service['autowiring_types'] as $autowiringType) {
|
||||
if (!is_string($autowiringType)) {
|
||||
throw new InvalidArgumentException(sprintf('A "autowiring_types" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file));
|
||||
}
|
||||
|
||||
$definition->addAutowiringType($autowiringType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->container->setDefinition($id, $definition);
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,7 @@
|
||||
<xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="autowiring-type" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="id" type="xsd:string" />
|
||||
<xsd:attribute name="class" type="xsd:string" />
|
||||
@ -103,6 +104,7 @@
|
||||
<xsd:attribute name="decorates" type="xsd:string" />
|
||||
<xsd:attribute name="decoration-inner-name" type="xsd:string" />
|
||||
<xsd:attribute name="decoration-priority" type="xsd:integer" />
|
||||
<xsd:attribute name="autowire" type="boolean" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="tag">
|
||||
|
@ -0,0 +1,300 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\AutowirePass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class AutowirePassTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testProcess()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('foo', __NAMESPACE__.'\Foo');
|
||||
$barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
|
||||
$barDefinition->setAutowired(true);
|
||||
|
||||
$pass = new AutowirePass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertCount(1, $container->getDefinition('bar')->getArguments());
|
||||
$this->assertEquals('foo', (string) $container->getDefinition('bar')->getArgument(0));
|
||||
}
|
||||
|
||||
public function testProcessAutowireParent()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('b', __NAMESPACE__.'\B');
|
||||
$cDefinition = $container->register('c', __NAMESPACE__.'\C');
|
||||
$cDefinition->setAutowired(true);
|
||||
|
||||
$pass = new AutowirePass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertCount(1, $container->getDefinition('c')->getArguments());
|
||||
$this->assertEquals('b', (string) $container->getDefinition('c')->getArgument(0));
|
||||
}
|
||||
|
||||
public function testProcessAutowireInterface()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('f', __NAMESPACE__.'\F');
|
||||
$gDefinition = $container->register('g', __NAMESPACE__.'\G');
|
||||
$gDefinition->setAutowired(true);
|
||||
|
||||
$pass = new AutowirePass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertCount(2, $container->getDefinition('g')->getArguments());
|
||||
$this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(0));
|
||||
$this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(1));
|
||||
}
|
||||
|
||||
public function testCompleteExistingDefinition()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('b', __NAMESPACE__.'\B');
|
||||
$container->register('f', __NAMESPACE__.'\F');
|
||||
$hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument(new Reference('b'));
|
||||
$hDefinition->setAutowired(true);
|
||||
|
||||
$pass = new AutowirePass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertCount(2, $container->getDefinition('h')->getArguments());
|
||||
$this->assertEquals('b', (string) $container->getDefinition('h')->getArgument(0));
|
||||
$this->assertEquals('f', (string) $container->getDefinition('h')->getArgument(1));
|
||||
}
|
||||
|
||||
public function testCompleteExistingDefinitionWithNotDefinedArguments()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('b', __NAMESPACE__.'\B');
|
||||
$container->register('f', __NAMESPACE__.'\F');
|
||||
$hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument('')->addArgument('');
|
||||
$hDefinition->setAutowired(true);
|
||||
|
||||
$pass = new AutowirePass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertCount(2, $container->getDefinition('h')->getArguments());
|
||||
$this->assertEquals('b', (string) $container->getDefinition('h')->getArgument(0));
|
||||
$this->assertEquals('f', (string) $container->getDefinition('h')->getArgument(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
|
||||
* @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a".
|
||||
*/
|
||||
public function testTypeCollision()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('c1', __NAMESPACE__.'\CollisionA');
|
||||
$container->register('c2', __NAMESPACE__.'\CollisionB');
|
||||
$aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired');
|
||||
$aDefinition->setAutowired(true);
|
||||
|
||||
$pass = new AutowirePass();
|
||||
$pass->process($container);
|
||||
}
|
||||
|
||||
public function testWithTypeSet()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('c1', __NAMESPACE__.'\CollisionA');
|
||||
$container->register('c2', __NAMESPACE__.'\CollisionB')->addAutowiringType(__NAMESPACE__.'\CollisionInterface');
|
||||
$aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired');
|
||||
$aDefinition->setAutowired(true);
|
||||
|
||||
$pass = new AutowirePass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertCount(1, $container->getDefinition('a')->getArguments());
|
||||
$this->assertEquals('c2', (string) $container->getDefinition('a')->getArgument(0));
|
||||
}
|
||||
|
||||
public function testCreateDefinition()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$coopTilleulsDefinition = $container->register('coop_tilleuls', __NAMESPACE__.'\LesTilleuls');
|
||||
$coopTilleulsDefinition->setAutowired(true);
|
||||
|
||||
$pass = new AutowirePass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertCount(1, $container->getDefinition('coop_tilleuls')->getArguments());
|
||||
$this->assertEquals('autowired.symfony\component\dependencyinjection\tests\compiler\dunglas', $container->getDefinition('coop_tilleuls')->getArgument(0));
|
||||
|
||||
$dunglasDefinition = $container->getDefinition('autowired.symfony\component\dependencyinjection\tests\compiler\dunglas');
|
||||
$this->assertEquals(__NAMESPACE__.'\Dunglas', $dunglasDefinition->getClass());
|
||||
$this->assertFalse($dunglasDefinition->isPublic());
|
||||
$this->assertCount(1, $dunglasDefinition->getArguments());
|
||||
$this->assertEquals('autowired.symfony\component\dependencyinjection\tests\compiler\lille', $dunglasDefinition->getArgument(0));
|
||||
|
||||
$lilleDefinition = $container->getDefinition('autowired.symfony\component\dependencyinjection\tests\compiler\lille');
|
||||
$this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass());
|
||||
}
|
||||
|
||||
public function testResolveParameter()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->setParameter('class_name', __NAMESPACE__.'\Foo');
|
||||
$container->register('foo', '%class_name%');
|
||||
$barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
|
||||
$barDefinition->setAutowired(true);
|
||||
|
||||
$pass = new AutowirePass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertEquals('foo', $container->getDefinition('bar')->getArgument(0));
|
||||
}
|
||||
|
||||
public function testOptionalParameter()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('a', __NAMESPACE__.'\A');
|
||||
$container->register('foo', __NAMESPACE__.'\Foo');
|
||||
$optDefinition = $container->register('opt', __NAMESPACE__.'\OptionalParameter');
|
||||
$optDefinition->setAutowired(true);
|
||||
|
||||
$pass = new AutowirePass();
|
||||
$pass->process($container);
|
||||
|
||||
$definition = $container->getDefinition('opt');
|
||||
$this->assertNull($definition->getArgument(0));
|
||||
$this->assertEquals('a', $definition->getArgument(1));
|
||||
$this->assertEquals('foo', $definition->getArgument(2));
|
||||
}
|
||||
|
||||
public function testDontTriggeruAutowiring()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('foo', __NAMESPACE__.'\Foo');
|
||||
$container->register('bar', __NAMESPACE__.'\Bar');
|
||||
|
||||
$pass = new AutowirePass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertCount(0, $container->getDefinition('bar')->getArguments());
|
||||
}
|
||||
}
|
||||
|
||||
class Foo
|
||||
{
|
||||
}
|
||||
|
||||
class Bar
|
||||
{
|
||||
public function __construct(Foo $foo)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class A
|
||||
{
|
||||
}
|
||||
|
||||
class B extends A
|
||||
{
|
||||
}
|
||||
|
||||
class C
|
||||
{
|
||||
public function __construct(A $a)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
interface DInterface
|
||||
{
|
||||
}
|
||||
|
||||
interface EInterface extends DInterface
|
||||
{
|
||||
}
|
||||
|
||||
class F implements EInterface
|
||||
{
|
||||
}
|
||||
|
||||
class G
|
||||
{
|
||||
public function __construct(DInterface $d, EInterface $e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class H
|
||||
{
|
||||
public function __construct(B $b, DInterface $d)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
interface CollisionInterface
|
||||
{
|
||||
}
|
||||
|
||||
class CollisionA implements CollisionInterface
|
||||
{
|
||||
}
|
||||
|
||||
class CollisionB implements CollisionInterface
|
||||
{
|
||||
}
|
||||
|
||||
class CannotBeAutowired
|
||||
{
|
||||
public function __construct(CollisionInterface $collision)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class Lille
|
||||
{
|
||||
}
|
||||
|
||||
class Dunglas
|
||||
{
|
||||
public function __construct(Lille $l)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class LesTilleuls
|
||||
{
|
||||
public function __construct(Dunglas $k)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class OptionalParameter
|
||||
{
|
||||
public function __construct(CollisionInterface $c = null, A $a, Foo $f = null)
|
||||
{
|
||||
}
|
||||
}
|
@ -890,6 +890,19 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertTrue($classInList);
|
||||
}
|
||||
|
||||
public function testAutowiring()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('a', __NAMESPACE__.'\A');
|
||||
$bDefinition = $container->register('b', __NAMESPACE__.'\B');
|
||||
$bDefinition->setAutowired(true);
|
||||
|
||||
$container->compile();
|
||||
|
||||
$this->assertEquals('a', (string) $container->getDefinition('b')->getArgument(0));
|
||||
}
|
||||
}
|
||||
|
||||
class FooClass
|
||||
@ -903,3 +916,14 @@ class ProjectContainer extends ContainerBuilder
|
||||
throw new InactiveScopeException('foo', 'request');
|
||||
}
|
||||
}
|
||||
|
||||
class A
|
||||
{
|
||||
}
|
||||
|
||||
class B
|
||||
{
|
||||
public function __construct(A $a)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -394,4 +394,25 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertSame($def, $def->setProperty('foo', 'bar'));
|
||||
$this->assertEquals(array('foo' => 'bar'), $def->getProperties());
|
||||
}
|
||||
|
||||
public function testAutowired()
|
||||
{
|
||||
$def = new Definition('stdClass');
|
||||
$this->assertFalse($def->isAutowired());
|
||||
$def->setAutowired(true);
|
||||
$this->assertTrue($def->isAutowired());
|
||||
}
|
||||
|
||||
public function testTypes()
|
||||
{
|
||||
$def = new Definition('stdClass');
|
||||
|
||||
$this->assertEquals(array(), $def->getAutowiringTypes());
|
||||
$this->assertSame($def, $def->setAutowiringTypes(array('Foo')));
|
||||
$this->assertEquals(array('Foo'), $def->getAutowiringTypes());
|
||||
$this->assertSame($def, $def->addAutowiringType('Bar'));
|
||||
$this->assertTrue($def->hasAutowiringType('Bar'));
|
||||
$this->assertSame($def, $def->removeAutowiringType('Foo'));
|
||||
$this->assertEquals(array('Bar'), $def->getAutowiringTypes());
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
<service id="foo" class="Foo">
|
||||
<autowiring-type>Bar</autowiring-type>
|
||||
<autowiring-type>Baz</autowiring-type>
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
@ -0,0 +1,6 @@
|
||||
<?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>
|
||||
<service id="bar" class="Bar" autowire="true" />
|
||||
</services>
|
||||
</container>
|
@ -0,0 +1,5 @@
|
||||
services:
|
||||
foo_service:
|
||||
class: FooClass
|
||||
# types is not an array
|
||||
autowiring_types: 1
|
@ -0,0 +1,5 @@
|
||||
services:
|
||||
foo_service:
|
||||
class: FooClass
|
||||
# autowiring_types is not a string
|
||||
autowiring_types: [ 1 ]
|
@ -0,0 +1,8 @@
|
||||
services:
|
||||
foo_service:
|
||||
class: FooClass
|
||||
autowiring_types: [ Foo, Bar ]
|
||||
|
||||
baz_service:
|
||||
class: Baz
|
||||
autowiring_types: Foo
|
@ -0,0 +1,4 @@
|
||||
services:
|
||||
bar_service:
|
||||
class: BarClass
|
||||
autowire: true
|
@ -494,4 +494,22 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertSame('Baz', $barConfigurator[0]->getClass());
|
||||
$this->assertSame('configureBar', $barConfigurator[1]);
|
||||
}
|
||||
|
||||
public function testType()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
|
||||
$loader->load('services22.xml');
|
||||
|
||||
$this->assertEquals(array('Bar', 'Baz'), $container->getDefinition('foo')->getAutowiringTypes());
|
||||
}
|
||||
|
||||
public function testAutowire()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
|
||||
$loader->load('services23.xml');
|
||||
|
||||
$this->assertTrue($container->getDefinition('bar')->isAutowired());
|
||||
}
|
||||
}
|
||||
|
@ -282,4 +282,41 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals(array(true), $definition->getArguments());
|
||||
$this->assertEquals(array('manager' => array(array('alias' => 'user'))), $definition->getTags());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function testTypesNotArray()
|
||||
{
|
||||
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
|
||||
$loader->load('bad_types1.yml');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function testTypeNotString()
|
||||
{
|
||||
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
|
||||
$loader->load('bad_types2.yml');
|
||||
}
|
||||
|
||||
public function testTypes()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
|
||||
$loader->load('services22.yml');
|
||||
|
||||
$this->assertEquals(array('Foo', 'Bar'), $container->getDefinition('foo_service')->getAutowiringTypes());
|
||||
$this->assertEquals(array('Foo'), $container->getDefinition('baz_service')->getAutowiringTypes());
|
||||
}
|
||||
|
||||
public function testAutowire()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
|
||||
$loader->load('services23.yml');
|
||||
|
||||
$this->assertTrue($container->getDefinition('bar_service')->isAutowired());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user