A Symfony Service Locator can be used to avoid the need to inject the complete container. The locator acts like a container but only has access to a limited number of services.
It takes a bit of magic to configure everything. In your case, you only want the locator to access services implementing the Doing interface.
Start with the locator which will inherit get/has methods like all containers:
use SymfonyComponentDependencyInjectionServiceLocator;
class DoingLocator extends ServiceLocator
{
protected $services = [
'bar' => 'app.service.bar',
'baz' => 'app.service.baz'
];
public function locate($string) {
return $this->get($this->services[$string]);
}
}
Now comes the magic. You actually could configure this manually in services.yaml per the documentation but it is more fun to do it automatically.
Start by making your Kernel class a compiler pass:
# src/Kernel.php
use SymfonyComponentDependencyInjectionCompilerCompilerPassInterface;
class Kernel extends BaseKernel implements CompilerPassInterface
Next, automatically tag all services that implement the Doing interface:
# Kernel.php
protected function build(ContainerBuilder $container)
{
$container->registerForAutoconfiguration(Doing::class)
->addTag('doing');
}
Finally, add a compiler pass to build your locator service:
# Kernel.php
public function process(ContainerBuilder $container)
{
$doingLocatorIds = [];
foreach ($container->findTaggedServiceIds('doing') as $id => $tags) {
$doingLocatorIds[$id] = new Reference($id);
}
$doingLocator = $container->getDefinition(DoingLocator::class);
$doingLocator->setArguments([$doingLocatorIds]);
}
And presto. You are done. You can now inject your DoingLocator (aka MyServiceFactory) and all should be well.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…