php - 通过可迭代注入(inject)的标记服务中的类名获取服务

标签 php symfony autowired symfony-dependency-injection

我正在努力通过类名从一组注入(inject)的标记服务中获取特定服务。

举个例子: 我将所有实现 DriverInterface 的服务标记为 app.driver 并将其绑定(bind)到 $drivers 变量。

在其他一些服务中,我需要获取所有标记为 app.driver 的驱动程序,然后实例化并仅使用其中的少数驱动程序。但需要的驱动程序是动态的。

服务.yml

_defaults:
        autowire: true
        autoconfigure: true
        public: false
        bind:
            $drivers: [!tagged app.driver]

_instanceof:
        DriverInterface:
            tags: ['app.driver']

一些其他服务:

/**
 * @var iterable
 */
private $drivers;

/**
 * @param iterable $drivers
 */
public function __construct(iterable $drivers) 
{
    $this->drivers = $drivers;
}

public function getDriverByClassName(string $className): DriverInterface
{
    ????????
}

因此,实现DriverInterface 的服务将作为可迭代结果注入(inject)到$this->drivers 参数中。我只能通过它们foreach,但是所有服务都将被实例化。

是否有其他方法可以注入(inject)这些服务以通过类名从中获取特定服务而无需实例化其他服务?

我知道有可能公开这些驱动程序并改用容器,但如果可以通过其他方式进行,我想避免将容器注入(inject)服务。

最佳答案

您不再(自 Symfony 4 起)需要创建编译器传递来配置服务定位器。

可以通过配置完成所有事情,让 Symfony 执行“魔法”。

您可以在配置中添加以下内容:

services:
  _instanceof:
    DriverInterface:
      tags: ['app.driver']
      lazy: true

  DriverConsumer:
    arguments:
      - !tagged_locator
        tag: 'app.driver'

需要访问这些而不是接收 iterable 的服务接收 ServiceLocatorInterface:

class DriverConsumer
{
    private $drivers;
    
    public function __construct(ServiceLocatorInterface $locator) 
    {
        $this->locator = $locator;
    }
    
    public function foo() {
        $driver = $this->locator->get(Driver::class);
        // where Driver is a concrete implementation of DriverInterface
    }
}

就是这样。您不需要任何其他东西,它就可以正常工作tm


完整示例

包含所有相关类的完整示例。

我们有:

FooInterface:

interface FooInterface
{
    public function whoAmI(): string;
}

AbstractFoo

为了简化实现,我们将在具体服务中扩展一个抽象类:

abstract class AbstractFoo implements FooInterface
{
    public function whoAmI(): string {
        return get_class($this);
    }   
}

服务实现

一些实现FooInterface的服务

class FooOneService extends AbstractFoo { }
class FooTwoService extends AbstractFoo { }

服务的消费者

另一个服务需要服务定位器来使用我们刚刚定义的这两个服务:

class Bar
{
    /**
     * @var \Symfony\Component\DependencyInjection\ServiceLocator
     */
    private $service_locator;

    public function __construct(ServiceLocator $service_locator) {
        $this->service_locator = $service_locator;
    }

    public function handle(): string {
        /** @var \App\Test\FooInterface $service */
        $service = $this->service_locator->get(FooOneService::class);

        return $service->whoAmI();
    }
}

配置

唯一需要的配置是这样的:

services:
  _instanceof:
    App\Test\FooInterface:
      tags: ['test_foo_tag']
      lazy: true
    
  App\Test\Bar:
      arguments:
        - !tagged_locator
          tag: 'test_foo_tag'
            

服务名称的 FQCN 替代方案

如果您不想使用类名来定义自己的服务名称,则可以使用静态方法来定义服务名称。配置将更改为:

App\Test\Bar:
        arguments:
          - !tagged_locator
            tag: 'test_foo_tag'
            default_index_method: 'fooIndex'

其中 fooIndex 是在每个返回字符串的服务上定义的公共(public)静态方法。注意:如果您使用此方法,您将无法通过类名获取服务。

关于php - 通过可迭代注入(inject)的标记服务中的类名获取服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54946647/

相关文章:

php - 是否可以使用 MySQL 阻止 mysql_real_escape_string 将 NULL 转换为 0?

php - 解析 Zillow 和其他 API 网站中的 XML 数据

php - PHP 中有类似宏的东西吗?或者 : How to make an own include function?

Symfony2 - Controller 中的 Composer 类加载器实例

php - Symfony2 未登录产品

php - 如何从查询中的日期转换时间?

php - 如何将 $_SERVER 变量注入(inject)到服务中

java - 找不到依赖关系的合格 bean 类型

filter - Java 8 多个过滤器上的流过滤器

spring - 在 Spring 中 Autowiring 注释而不使用组件扫描