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, если он находится в том же запросе, у вас должен быть один и тот же экземпляр вашего сервиса.
В контейнере сервисов все сервисы являются общими по умолчанию. Это означает, что каждый раз, когда вы получаете сервис, вы получаете один и тот же экземпляр.
Если вы не нашли решения для этого, у вас есть две альтернативы для хранения ваших данных. Сессия для простых форматов и база данных для более сложного и длительного хранения.