4

I have setup dependencies and definitions while configuring the container using the ContainerBuilder and then compiling it to get the actual Container, but whenever I try injecting dependencies they always tend to be ignored.

Have I missed the concept of the injectOn() method, or am I doing something wrong here ($this->translator remains unassigned)? I've tried different approaches to this, both instantiating the class and adding the object to the ContainerBuilder and also passing it as a \DI\object() definition, both with the same result.

<?php
include "../vendor/autoload.php";

class Translator
{}

class TextHandle
{
    protected $translator;

    public function setTranslator (Translator $translator)
    {
        $this->translator = $translator;
    }

    public function test ()
    {
        var_dump($this->translator);
    }
}

class TestCase
{
    protected $translator;

    public function __construct (Translator $translator)
    {
        $this->translator = $translator;
    }
}

// Setup container
$containerBuilder = new \DI\ContainerBuilder();
$containerBuilder->addDefinitions([
    Translator::class => \DI\object(),
]);

$container = $containerBuilder->build();

// Create object and inject
$textHandle = new TextHandle();
$container->injectOn($textHandle);

// Test injection, fails
$textHandle->test(); // NULL

// Test access, works
$translator = $container->get(Translator::class);
var_dump($translator); // object(Translator)#18 (0) {}

// Try having the container instantiate
$textHandle = $container->make(TextHandle::class);
$textHandle->test(); // Null

// Try object with constructor, works
$testCase = $container->make(TestCase::class);
var_dump($testCase); // Translator is set
Daniel
  • 55
  • 4

1 Answers1

4

According to the documentation, PHP-DI does not perform autowiring through setters.

You should add the definition for TextHandle class and configure it to inject Translator through the setter:

$containerBuilder->addDefinitions([
    TextHandle::class => \DI\object()->method('setTranslator', \DI\get(Translator::class))
]);
Bartosz Zasada
  • 3,762
  • 2
  • 19
  • 25
  • The manual section you linked refers to the scenario where the container instantiate an object without type hinted constructor arguments. My object has no constructor, and is manually instantiated. Does it still apply? **Edit:** http://php-di.org/doc/inject-on-instance.html This manual section says `Now, $object has all its dependencies injected (through setter injections and property injections).` – Daniel Nov 25 '16 at 15:31
  • The key point here is "`setLogger()` will not be called". This makes me assume that autowiring does not work with setters, but only with constructors. It's a bad practice to manually instantiate classes that should have their dependencies injected. You should get `TextHandle` instance from container. – Bartosz Zasada Nov 25 '16 at 15:37
  • To answer your edit: I think in this case you must have proper configuration of the injections in the container. Try adding the definition in the way I described it before calling `injectOn()`. Also, the sentence "in some situations, you don't have the control of the creation of an object" is very perplexing to me, I have no idea how that would be possible. – Bartosz Zasada Nov 25 '16 at 15:41
  • Well, sometimes they are not dependencies but options, which is my scenario, the translator is optional. It seems as the PHP-DI only resolves constructor arguments in that case, even though it has indexed all setter methods correctly if you dump the cache build and look at it. See my updated question code sample, the translator is only set if its a constructor argument. I guess I read the manual wrong. I'm just wondering why the `injectOn` method is there in the first place then. – Daniel Nov 25 '16 at 16:02
  • Hi @Daniel, PHP-DI author here. Setters are not called automatically because it's impossible for PHP-DI to guess which methods are setter for DI (should be called) and which are not (should *not* be called). That's why you have to configure setters manually (or with annotations). And FYI `injectOn()` works the same way as `make()`. Maybe there's something we could improve in the documentation? – Matthieu Napoli Nov 28 '16 at 08:18
  • @MatthieuNapoli Thank you for replying. Is it possible to tell the container to inject on a super-type instead of defining all possible classes for a single injection? Right now I'm defining a lot of entries, and I must say this is the first DI im using, but it feels to me like I'm repeating myself even though classes are utilizing the same `trait`. I.e; `class TextHandle { use TranslatorAwareTrait; }` thus having defined the injection procedure for the trait, instead of the "child". – Daniel Dec 01 '16 at 09:55
  • @Daniel no it's not possible with PHP-DI. To be honest for me it's sort of "a feature" in the sense that injecting dependencies everywhere like that sounds like a code smell, but of course it always depends on the context. – Matthieu Napoli Dec 03 '16 at 11:11