PHP/Symfony: почему TwigExtension получает иную версию службы, отличную от контроллера?

Версии:

  • Версия Symfony: 4.2
  • Версия Twig: 2.6.2

Фон

  • Я обновляю существующее PHP-приложение с довольно старой версии Symfony (2.x) до 4.2.

В чем дело?

У меня есть общий сервис, который вводится и используется в моем контроллере.

Существует также TwigExtension, который получает тот же сервис и ожидает точно такой же экземпляр сервиса.

  • Почему TwigExtension полагается, что служба в любом случае является одним и тем же экземпляром?
    • Сервис содержит конкретные данные, которые обрабатывает TwigExtension. По сути, в настоящее время эта служба, по-видимому, используется в качестве своего рода конкретного глобального контейнера данных или сборщика данных.
  • В этот момент вы можете утверждать, что это звучит как сомнительная практика, и спрашивать, почему я на самом деле делаю что-то подобное.
    • Я имею дело с существующим приложением, с существующим функционалом в зависимости от описанного поведения.
    • Я был бы рад получить приложение, работающее на первом этапе, и изменить подозрительные обходные пути на втором.

Ожидаемое поведение:

  • Я ожидаю, что общий сервис будет точно создан один раз за запрос, и поэтому я также ожидаю, что TwigExtension получит тот же экземпляр, что и контроллер.

Фактическое поведение:

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

Поведение в старой версии (Symfony 2.x):

  • TwigExtension получил точно такой же экземпляр службы.

Что я пробовал:

  • Я попробовал это как с помощью конструктора-инъекции, так и с получением службы по телефону $container->get('service') в TwigExtension
  • Я попробовал это с public: true / false
  • Я пробовал это в других местах, таких как слушатели событий, где тот же экземпляр службы используется, как и ожидалось.

Мои вопросы:

  • Почему TwigExtension получает другой экземпляр службы, чем контроллер?
    • Почему существует другое поведение между TwigExtensions и, например, прослушивателями событий?
  • Какие общие исключения существуют для вышеописанного ожидаемого поведения?
  • Можете ли вы указать мне любую полезную документацию? (конечно я много гуглил и читал соответствующую документацию на сайте symfony, но может я что-то упустил?)
  • Можете ли вы порекомендовать другой способ достижения чего-то похожего?

2 ответа

Решение

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

Один сервис был определен вручную в services.yml с идентификатором сервиса "service". Определение сервисов вручную было единственным способом определения сервисов еще во времена Symfony 2.x.

Затем появился autowire и в основном определяет сервисы с идентификаторами их имен классов. Мы смогли убедиться, что это действительно проблема, используя "bin / console debug: container". Это является следствием обновления до Symfony 3.4+ и включения autowire.

Помимо создания сервисов, autowire также позволяет вводить сервис, печатая на клавиатуре имя класса. Если в контейнере найден соответствующий идентификатор сервиса, то сервис внедряется. Таким образом, в этом случае расширение ветки, вероятно, было изменено на использование ввода текста, в то время как контроллер использует $container->get, что привело к внедрению двух разных сервисов. Или, возможно, наоборот.

Одним из исправлений будет использование псевдонима:

# services.yml
MyServiceClassName: service

Псевдоним будет в основном подавлять автоматическое создание второго сервиса.

"Лучший" подход (или наименее рекомендуемый) заключается в том, чтобы прекратить использование $container->get и просто внедрить службу там, где это необходимо. Затем вы полностью отбросите определение "сервис".

И последнее замечание: если бы самой службе потребовались какие-либо аргументы конструктора масштабирующего устройства (строки или целые числа), то процесс автопроводки завершился бы ошибкой с сообщением об ошибке. Эти ошибки могут быть изначально довольно запутанными, потому что у вас уже определен работающий сервис, но поскольку идентификатор сервиса отличается, autowire просто пашет вперед и пытается создать новый.

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

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

Если вы не нашли решения для этого, у вас есть две альтернативы для хранения ваших данных. Сессия для простых форматов и база данных для более сложного и длительного хранения.

Другие вопросы по тегам