Symfony: Injecting all the Classes from a Folder

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!