Служба Symfony запускается только после вызова метода

#symfony #dependency-injection

#symfony #внедрение зависимостей

Вопрос:

Я заметил, что служба Symfony запускается только (выполняется конструктор), когда вызывается метод в этой службе. Это может быть важно, если в вашей службе есть только конструктор и нет методов.

Например:

 class MyService {

    public function __construct($someOtherService) {

        $someOtherService->setFoo("bar");

    }
}

// And of course put this service in services.yml

app.my_service:
    class: AppBundle...MyService
    arguments: [ app.some_other_service ]
  

В этом случае конструктор и, следовательно, setFoo(«bar») не вызывается. Почему это так? Можно ли каким-то образом принудительно инициировать службу, не вызывая (фиктивный) метод в этой службе?

Я также попытался добавить «lazy: false» для app.my_service, но это не имеет никакого значения.

Я использую Symfony 2.8.

Комментарии:

1. Вы что-то неправильно интерпретируете. Конструктор вызывается, как только вы получаете службу из контейнера. Просто определение службы в службах. yml не приводит к созданию экземпляра службы.

2. Да, теперь я понимаю. И я хотел загрузить службу, не получая ее из контейнера.

Ответ №1:

ваши службы никогда не создаются, если они никогда не используются. для принудительного создания службы вы можете подключаться к любому типу прослушивателей событий, который подходит вашему событию, когда вы хотите получить сервис (т.е. kernel.request ) и передать эту службу в качестве зависимости слушателю. это приведет к запуску конструктора службы при первом запуске события в течение срока службы контейнера.

но я бы предпочел предложить вам ознакомиться с архитектурой. наличие службы только с конструктором — это бессмыслица

Более того, вы можете создавать экземпляры служб при создании экземпляра EvendDispatcher (просто потому, что это будет зависеть от вашей службы) без запуска событий

пример прослушивателя:

 class ServiceInstantiatorListener
{
  public function onRequest(KernelEvent $kernel)
  {
     return; //noop, just make sure it works
  }

  public function instantiate($service)
  {
     return $service; // noop again, just call to pass service container argument
  }
}
  

конфигурация yaml:

 services:
  my_app.service_instantiator_listener:
    class: MyAppServiceInstantiatorListener
    tags:
    - { 'name': 'kernel_events', 'event': 'kernel.request', 'method':'onRequest' }
    calls:
    - [instantiate, ["@my_app.weird_service_one"]]
    - [instantiate, ["@my_app.weird_service_two"]]
  

Идя дальше, вы можете пометить свои службы тегом и динамически настраивать вызовы с MyAppBundleExtension помощью проходов компилятора

http://symfony.com/doc/current/service_container/tags.html#create-a-compiler-pass

Я надеюсь, что есть лучшие способы принудительно создавать экземпляры сервисов (например, некоторые внутренние события контейнера), но в настоящее время я не встречал случая, когда мне это нужно.

Комментарии:

1. Да! Я уже рассматривал возможность использования прослушивателей событий для поиска решения. Я понимаю, что вы имеете в виду, и обычно я не использую подобные сервисы, но это крайний случай.

2. Спасибо, я использовал ваш пример для этого. Хотя я не использовал «создание экземпляра», но в качестве аргумента я предоставляю контейнер службы, и в слушателе я вызвал обе мои две странные службы 😉

3. Да, но это задерживает создание экземпляра до запуска события, поэтому зависит от того, что вам нужно

Ответ №2:

Вы описываете поведение службы с отложенной загрузкой. Проверьте конфигурацию lazy: true и удалите / отключите ее.

Symfony docs, служба с отложенной загрузкой:

Фактический класс будет создан, как только вы попытаетесь взаимодействовать со службой (например, вызвать один из ее методов).

Комментарии:

1. Да, я забыл упомянуть, что я уже пытался добавить «lazy: false» для app.my_service, но это не сработало. Я добавлю это в свой пост.

Ответ №3:

На самом деле вам не нужно вызывать фиктивный метод в службе, чтобы инициировать его. Вы можете создать экземпляр объекта service, используя следующий оператор:

$this->container()-> get(‘app.my_service’);

Комментарии:

1. Это я знал, я хотел, чтобы служба загружалась без этого вызова.