Подтвердить что ты не робот

Циркулярная ссылка при вводе контекста безопасности в (слушатель субъекта) класса

Здесь было 2 вопроса, которые должны были решить проблему инъекции всего контейнера. Но вопрос... см. Ниже (разница примечаний между попытками 2 и 3)...

Попробуйте 1

public function __construct(SecurityContext $securityContext) {
    $this->securityContext = $securityContext);  
}  

Справочник по куркулам. Хорошо...

Попробуйте 2

public function __construct(ContainerInterface $container) {
    $this->securityContext = $container->get('security.context');  
}  

Циркулярная ссылка ( Почему?, я вставляю контейнер, как в try 3, за исключением того, что я получил только контекст безопасности)

Попробуйте 3

public function __construct(ContainerInterface $container) {
    $this->container = $container;  
}  

Works.

4b9b3361

Ответ 1

Это происходит потому, что ваш контекст безопасности зависит от этого слушателя, возможно, через диспетчер объектов, который вводится в провайдер пользователя. Лучшим решением является инжекция контейнера в слушатель и ленивый доступ к контексту безопасности.

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

Ответ 2

Начиная с Symfony 2.6 эта проблема должна быть исправлена. Запрос на растяжение только что был принят в мастер. Ваша проблема описана здесь. https://github.com/symfony/symfony/pull/11690

Как и в Symfony 2.6, вы можете ввести security.token_storage в ваш слушатель. Эта служба будет содержать токен, используемый SecurityContext в <= 2.5. В 3.0 эта служба заменит SecurityContext::getToken() вообще. Вы можете увидеть основной список изменений здесь: http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements#deprecated-the-security-context-service

Пример использования в версии 2.6:

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

services:
    my.listener:
        class: EntityListener
        arguments:
            - "@security.token_storage"
        tags:
            - { name: doctrine.event_listener, event: prePersist }


Ваш слушатель

use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class EntityListener
{
    private $token_storage;

    public function __construct(TokenStorageInterface $token_storage)
    {
        $this->token_storage = $token_storage;
    }

    public function prePersist(LifeCycleEventArgs $args)
    {
        $entity = $args->getEntity();
        $entity->setCreatedBy($this->token_storage->getToken()->getUsername());
    }
}

Ответ 3

Причина "2" терпит неудачу, а "3" - нет, потому что в варианте 2 вы пытаетесь получить доступ к контексту безопасности непосредственно из контейнера, когда он, вероятно, еще не заполнен.

Насколько я могу судить, Symfony2 анализирует конфигурацию и создает службу один за другим, а затем переходит к обработке остальной части запроса.

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

Вы можете увидеть последствия этого, выполнив две службы. A и B. A передается B, а B передается A. Теперь у вас есть круговая ссылка. Если вы вместо этого передали контейнер в и B, вы не смогли бы получить доступ к A из B и B из A без проблем.

Ответ 4

Вы всегда должны стараться избегать инъекции контейнера непосредственно в свои службы.

Я думаю, что наилучшим решением проблемы "круговой ссылки", а также возможными проблемами производительности будет использование "" Lazy Services "", Начиная с Symfony 2.3.

Просто укажите свою зависимость как lazy в конфигурации вашего сервисного контейнера и установите мост ProxyManager (см. подробности в документации Lazy Services выше).

Я надеюсь, что это поможет, приветствует.