I have a PHP command line utility that is executed like below:
tool plugin command [options]...
The idea is that 3rd party plugins can be used, thus the main tool has no idea what they are. It can find them through an environment variable just like PATH
.
The main tool application has a composition root that contains dependencies used by all plugins (shared). However, each plugin has its own dependencies and as such, the plugin's constructor is where I have a second composition root.
Does this sound like a legitimate case for multiple composition roots?
$c = new DIContainer();
$c->bind(...); // whatever is needed
$loader = new PluginLoader($c);
// argv[1] is the plugin name. the loader will inject the Container
// into the plugin's constructor. This is the 2nd composition root
$plugin = $loader->get($argv[1]);// locates and constructs
// the plugin populates its commands as plugin.command
$cmd = $c[ $argv[1] . '.' . $argv[2] ];
$cmd->execute(array_splice($argv, 3));
second question
Each command has an array of Option objects and I am not sure if the Command should have this array pre-composed and injected or if an OptionFactory should be injected into the command's constructor. The latter is nice as it keeps options encapsulated within the command, vs the plugin's constructor having to build an option list for every command it supports. Note these are not the command line options ($argv
), its just the list of options supported by the command and includes a validator.
// Plugin class, 2nd composition root, called by PluginLoader
class MyPlugin{
public function __construct(Container $c){
$c->bind('myplugin.command', function(Container $c){
return new Command([ ... create option objects here ...]);
// or
return new Command($c['option-factory']); // let cmd build option[]
});
}
}