Skip to content

[DependencyInjection] Fix some edge cases with autowiring #16464

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 7, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ private function populateAvailableTypes()
*/
private function populateAvailableType($id, Definition $definition)
{
if (!$definition->getClass()) {
// Never use abstract services
if ($definition->isAbstract()) {
return;
}

Expand All @@ -147,6 +148,11 @@ private function populateAvailableType($id, Definition $definition)
$this->types[$type] = $id;
}

// Cannot use reflection if the class isn't set
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be moved above the foreach-loop as it doesn't depend on the loop.
And improves performance a little.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends of the class of the definition. It cannot be moved outside of the loop.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean like this:

    private function populateAvailableType($id, Definition $definition)
    {
        // Never use abstract services
        if ($definition->isAbstract()) {
            return;
        }

        // Cannot use reflection if the class isn't set
        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);
        }
    }

Check if a class is set, if not return before executing the loop.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done that on purpose. Associating a service with a type using the autowiring_types key doesn't require reflection, so it doesn't require the class key to exist.

It allows to do things like associating types with a service definition where the class is associated in a compiler pass executed after the autowiring.

if (!$definition->getClass()) {
return;
}

if ($reflectionClass = $this->getReflectionClass($id, $definition)) {
$this->extractInterfaces($id, $reflectionClass);
$this->extractAncestors($id, $reflectionClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ private function resolveDefinition(ContainerBuilder $container, DefinitionDecora
$def->setArguments($parentDef->getArguments());
$def->setMethodCalls($parentDef->getMethodCalls());
$def->setProperties($parentDef->getProperties());
$def->setAutowiringTypes($parentDef->getAutowiringTypes());
if ($parentDef->getFactoryClass(false)) {
$def->setFactoryClass($parentDef->getFactoryClass(false));
}
Expand Down Expand Up @@ -202,6 +203,11 @@ private function resolveDefinition(ContainerBuilder $container, DefinitionDecora
$def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
}

// merge autowiring types
foreach ($definition->getAutowiringTypes() as $autowiringType) {
$def->addAutowiringType($autowiringType);
}

// these attributes are always taken from the child
$def->setAbstract($definition->isAbstract());
$def->setScope($definition->getScope(false), false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ public function testOptionalParameter()
$this->assertEquals('foo', $definition->getArgument(2));
}

public function testDontTriggeruAutowiring()
public function testDontTriggerAutowiring()
{
$container = new ContainerBuilder();

Expand All @@ -216,6 +216,21 @@ public function testClassNotFoundThrowsException()
$pass = new AutowirePass();
$pass->process($container);
}

public function testDontUseAbstractServices()
{
$container = new ContainerBuilder();

$container->register('abstract_foo', __NAMESPACE__.'\Foo')->setAbstract(true);
$container->register('foo', __NAMESPACE__.'\Foo');
$container->register('bar', __NAMESPACE__.'\Bar')->setAutowired(true);

$pass = new AutowirePass();
$pass->process($container);

$arguments = $container->getDefinition('bar')->getArguments();
$this->assertSame('foo', (string) $arguments[0]);
}
}

class Foo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,26 @@ public function testDecoratedServiceCanOverwriteDeprecatedParentStatus()
$this->assertFalse($container->getDefinition('decorated_deprecated_parent')->isDeprecated());
}

public function testProcessMergeAutowiringTypes()
{
$container = new ContainerBuilder();

$container
->register('parent')
->addAutowiringType('Foo')
;

$container
->setDefinition('child', new DefinitionDecorator('parent'))
->addAutowiringType('Bar')
;

$this->process($container);

$def = $container->getDefinition('child');
$this->assertEquals(array('Foo', 'Bar'), $def->getAutowiringTypes());
}

protected function process(ContainerBuilder $container)
{
$pass = new ResolveDefinitionTemplatesPass();
Expand Down