rdlowrey / auryn

IoC Dependency Injector
MIT License
723 stars 65 forks source link

Multiple injector instances with shared dependencies #161

Closed andreasciamanna closed 1 year ago

andreasciamanna commented 7 years ago

I've been looking to all issues, but it seems like this question hasn't been asked yet.

First of all, I didn't try this library yet: I saw it mentioned and thought it might be helpful for my work.

However, I need to understand something before even starting playing with it.

Scenario: WordPress plugin development, with a main plugin + n add-ons (as separate plugins).

The main plugin has a "main" class. Some of the other add-ons classes may need to use the main class as a depenendecy.

The main class should not be instantiated multiple times.

Now, I guess I could store an instance of Auryn\Injector as a global variable and use that global variable with all other add-ons, but I don't want to use globals (I don't think I need to explain why) if there are better options.

I wonder if, setting a dependency as shared and creating multiple instances of Auryn\Injector (which is most likely what I would need to do anyway if I don't want to use the global variable approach) I would still be able to use this shared dependency.

Danack commented 7 years ago

I've thought about this problem, and although it's a perfectly valid thing to want to do, I haven't been able to come up with a solution internal to this library that wouldn't be one of:

i) Far too complicated. ii) Far too difficult to use. iii) Not covering enough use-cases. iv) all of the above.

For most of the valid but complex use-cases people have asked about, normally they can be solved by extending the Injector, and putting that complex logic in the make() method.

I think something along these lines should work for you:

$mainPluginInjector = new Injector();

// Configure the $mainPluginInjector here.
$mainPluginInjector->share(MainPlugin::class)

$upstreamClasses = [
    MainPlugin::class,
    MainLogger::class
];

$pluginInjector = new DownstreamInjector($injector, $upstreamClasses);

// configure $pluginInjector here.

// $pluginInjector is now ready to use.

class DownstreamInjector extends Injector {

    private $upstreamInjector;

    private $upstreamClasses = [];

    function __construct(Injector $upstreamInjector, array $upstreamClasses, Reflector $reflector = null) {
          parent::__construct($reflector)
     }

    public function make($name, array $args = array()) {
        // Arbitrarily complex logic could go here.
        if (in_array($name, $this->upstreamClasses, true) === true) {
            return $this->upstreamInjector->make($name, $args);
        }
        return parent::make($name, $args);
    }
}

If it doesn't feel free to specify your requirements a bit more clearly. Although a solution is unlikely to be added to this library itself, there's probably a not too complex way of doing it yourself.

overclokk commented 6 years ago

If I understand well this should work:

// main-plugin.php

add_action( 'plugin_loaded', function () {

    $injector = new \Auryn\Injector();
    $injector->make( 'MyPlugin\MainClass' );

    // do some other stuff with $injector obj

    add_filter( 'my_plugin_get_injector_instance', function() use ( $injector ) {
        return $injector;
    } );

} );

// Then in the add-on plugin you can

add_action( 'plugin_loaded', function () {

    $injector = apply_filters( 'my_plugin_get_injector_instance', null );

    if ( ! $injector ) {
        // do some stuff or return
    }

    $injector->make( 'MyAddon\ClassWithMainClassDependency' );

}, 11 );

// ClassWithMainClassDependency.php

namespace MyAddon;
use MyPlugin\MainClass;

class ClassWithMainClassDependency {
    __constructor ( MainClass $obj ) {
         $this->main_class = $obj;
    }
}
Danack commented 1 year ago

It's probable I misread the question. But I've now added some words in commit f93349d5dd98399aa0ac69d2c62ba7cc93e2f3b1


When app-bootstrapping by Auryn is not possible

Sometimes, the initialisation of the application is outside of your control. One example would be writing plugins for Wordpress, where Wordpress is initialising your plugin, not the other way round.

You can still use Auryn by using a function to make a single instance of the injector:

function getAurynInjector()
{
    static $injector = null;
    if ($injector == null) {
        $injector = new \Auryn\Injector();
        // Do injector defines/shares/aliases/delegates here
    }

    return $injector;
}