This started out as a question about a tool that supports plugins. How can you just automatically inject all the classes in the plug-ins folder? When really a better question would have been
How can Symfony's dependency injection automatically give me all the services implementing a certain interface?
about injection all the classes implementing an interface. In this case it would be the plugIn
interface being created for this tool. Imagine you've got a bunch of classes in a folder and they all look roughly like this:
class AlertSlackPlugin implements EnkPlugIn {
public function handle($event) {
# ... code ...
}
}
It turns out Symfony (4.4+) has given us a pretty easy way to inject all of these to a class that wants them. It takes a few steps:
- In services.yml define a rule to auto-tag every service using that interface
- In services.yml create a service definition for the class where they should be injected
- Update the main class's constructor
Auto-Tagging
First, update your services.yml
file to auto-tag every service it finds that implements the interface you've defined. You can use any string for the tag, but in this case I've repeated the name of the interface.
services:
_instanceof:
App\Service\PlugIns\EnkPlugIn
tags: ['EnkPlugIn']
Auto-Injecting
Next, create service definition for the class that wants all the plugins. You only need to define the one argument, you can continue to rely on automatic DI for the rest of your class
App\Service\PluginProcessor:
arguments:
$plugins: !tagged_iterator EnkPlugIn
The Receiving Class
Symfony will pass an iterable to your class, looking for the named argument you provided in the second step.
class PluginProcessor {
public function __construct(ServiceX $x, iterable $plugins) {
# ... code ....
}
}
And that's it!