thephpleague / container

Small but powerful dependency injection container
http://container.thephpleague.com
MIT License
841 stars 101 forks source link

Auto wiring a shared dependency #247

Open jtojnar opened 1 year ago

jtojnar commented 1 year ago

I tried using ReflectionDelegate to achieve auto-wiring and it works mostly great. The issue occurs when I register some services as shared – then they won’t be autowired any more.

See the following simplified example:

<?php

require __DIR__ . '/vendor/autoload.php';

class Logger {
    public function __construct(string $name) { }
}

class Authentication {
    public function __construct(Logger $logger) { }
}

class AboutController {
    public function __construct(Authentication $authentication) { }
}

$container = new League\Container\Container();

// Register the reflection container as a delegate to enable auto wiring.
$container->delegate(new League\Container\ReflectionContainer(true));

$container
    ->add(Authentication::class)
    ->setShared()
;

$container
    ->add(Logger::class)
    ->addArgument('selfoss')
    ->setShared()
;

var_dump($container->get(AboutController::class));
Fatal error: Uncaught ArgumentCountError: Too few arguments to function Authentication::__construct(), 0 passed and exactly 1 expected in test.php:10
Stack trace:
#0 [internal function]: Authentication->__construct()
#1 src/Definition/Definition.php(212): ReflectionClass->newInstanceArgs(Array)
#2 src/Definition/Definition.php(175): League\Container\Definition\Definition->resolveClass('Authentication')
#3 src/Definition/Definition.php(154): League\Container\Definition\Definition->resolveNew()
#4 src/Definition/DefinitionAggregate.php(79): League\Container\Definition\Definition->resolve()
#5 src/Container.php(175): League\Container\Definition\DefinitionAggregate->resolve('Authentication')
#6 src/Container.php(118): League\Container\Container->resolve('Authentication')
#7 src/Argument/ArgumentResolverTrait.php(45): League\Container\Container->get('Authentication')
#8 src/Argument/ArgumentResolverTrait.php(107): League\Container\ReflectionContainer->resolveArguments(Array)
#9 src/ReflectionContainer.php(58): League\Container\ReflectionContainer->reflectArguments(Object(ReflectionMethod), Array)
#10 src/Container.php(203): League\Container\ReflectionContainer->get('AboutController')
#11 src/Container.php(118): League\Container\Container->resolve('AboutController')
#12 test.php(33): League\Container\Container->get('AboutController')
#13 {main}
  thrown in test.php on line 10

I could addArguments the dependencies explicitly but doing it for all of the shared services would be annoying.

philipobenito commented 5 months ago

You actually don't need to setShared, by doing that, you're telling the container that Authentication::class has a definition in the container, so no autowiring is attempted. However, you have the reflection container set to cache resolutions, so anything resolved through it will, in effect, be shared anyway.