Level-2 / Dice

Dice - a lightweight Dependency Injection Container for PHP
https://r.je/dice
431 stars 68 forks source link

Injection of already instantiated object #159

Open chris97pl opened 5 years ago

chris97pl commented 5 years ago

Is there a way to inject already instantiated object so it can be resolved as shared with further $dice->create() calls?

From my quick glance at the code adding new entry in $instances array should do the trick. Unfortunately I cannot find the proper method to do this and $instances property is private so can't do it even by extending the Dice class.

Am I missing something?

garrettw commented 5 years ago

You'll want to check out the documentation on substitution rules.

TRPB commented 5 years ago

From a purely theoretical point of view, if you're using a DIC, the DIC would normally have the job of creating the instances so the answer would normally be to give dice the rule for constructing the object.

However, with that said, there are a few different ways to reference existing objects, depending on what you're trying to do. You can supply an existing instance either as constructParams or substitutions:

$instance = new MyInstance();

//Configure dice to substitute any type hinted `SomeInterface` with $instance
$dice = $dice->addRule('SomeClass', [
    'substitutions' => ['SomeInterface' => [\Dice\Dice::INSTANCE => $instance ] ]
]);

// Or pass it in as a constructor argument
$dice = $dice->addRule('SomeClass', [
    'constructParams' => [$instance]
]);

You can also set up a global substitution so it gets applied to any type-hinted constructor argument:

//Configure dice to substitute any type hinted `SomeInterface` with $instance
$dice = $dice->addRule('*', [
    'substitutions' => ['SomeInterface' => [\Dice\Dice::INSTANCE => $instance ] ]
]);

If you need more control, Dice also calls closures if they are provided as instances:

$dice = $dice->addRule('*', [
    'substitutions' => ['SomeInterface' => [\Dice\Dice::INSTANCE => function() use ($instance) {
                                      return $instance
                                    } ]
                               ]
]);

Finally, if your existing object is coming from a factory or another DIC you can use call with chaining:

$dice = $dice->addRule('Something', [
    'instanceOf' => 'Pimple'
    'call' => [
        ['get', ['SomeClass'], \Dice\Dice::CHAIN_CALL ]
    ]
]):

//Calls $pimple->get('SomeClass') and returns the instance
var_dump($dice->create('Something'))
chris97pl commented 5 years ago

That is brilliant. I was pretty sure I was missing something and I did. That works perfectly well. Thank you for explaining.

KarlAustin commented 4 years ago

Is this all still valid? As I've just tried this as my code is a module for someone else's software, so some objects I need are already instantiated, nothing I can do about that.

            $di = new \Dice\Dice();
            $di = $di->addRules([
                '*' => [
                    'substitutions' => [
                        'PDO' => [\Dice\Dice::INSTANCE => $db]
                    ],
                ]]);

Where $db is a valid PDO object from the main application.

When I do this, Dice errors with:

Uncaught TypeError: Argument 1 passed to Dice\Dice::create() must be of the type string, object given
dice.php(181): Dice\Dice->create(Object(PDO), Array)

Also tried it using closures:

            $di = $di->addRules([
                '*' => [
                    'substitutions' => [
                        'PDO' => [\Dice\Dice::INSTANCE => function() use ($db) {
                                      return $db;
                                    }]
                    ],
                ]]);

That gives me a PDO exception for invalid DSN - So it looks like it is still trying to construct a new PDO object rather than sub in mine.

Thanks.

garrettw commented 4 years ago

@KarlAustin All you need is

            $di = $di->addRules([
                '*' => [
                    'substitutions' => [
                        'PDO' => $db
                    ],
                ]]);

[\Dice\Dice::INSTANCE => $db] always fails because INSTANCE expects a class name string or a callable. I'm not sure about the latter error, but closures there are really only intended for constructing and returning new objects, since the syntax for using existing objects as I mention above is much simpler.

That said, why not allow Dice to create your PDO object on demand and mark it as shared?