12 votes

Référence circulaire lors de l'injection du contexte de sécurité dans la classe (Entity Listener)

Il y avait 2 questions ici disant que l'injection de l'ensemble du conteneur de service devrait résoudre ce problème. Mais question ... voir ci-dessous (notez la différence entre les essais 2 et 3) ...

Essai 1

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

Référence circulaire. Ok ...

Essai 2

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

Référence circulaire ( Pourquoi ? Je suis en train d'injecter le conteneur comme dans l'essai 3, sauf que je n'ai obtenu que le contexte de sécurité.)

Essai 3

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

Travaux.

24voto

Kris Wallsmith Points 4804

Cela se produit parce que votre contexte de sécurité dépend de cet écouteur, probablement via le gestionnaire d'entités injecté dans un fournisseur d'utilisateurs. La meilleure solution est d'injecter le conteneur dans le listener et d'accéder au contexte de sécurité paresseusement.

En général, je n'aime pas injecter le conteneur entier dans un service, mais je fais une exception avec les écouteurs Doctrine parce qu'ils sont chargés rapidement et devraient donc être aussi paresseux que possible.

8voto

Anyone Points 1386

A partir de Symfony 2.6, ce problème devrait être corrigé. Une pull request vient d'être acceptée dans le master. Votre problème est décrit ici. https://github.com/symfony/symfony/pull/11690

À partir de Symfony 2.6, vous pouvez injecter l'option security.token_storage à votre auditeur. Ce service contiendra le jeton tel qu'il est utilisé par l'application SecurityContext en <=2.5. En 3.0, ce service remplacera le SecurityContext::getToken() tout à fait. Vous pouvez voir une liste des changements de base ici : http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements#deprecated-the-security-context-service

Exemple d'utilisation en 2.6 :

Votre configuration :

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

Votre auditeur

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());
    }
}

1voto

Wpigott Points 473

La raison pour laquelle l'option 2 échoue et l'option 3 non, est que dans l'option 2, vous essayez d'accéder au contexte de sécurité immédiatement à partir du conteneur alors qu'il n'est probablement pas encore rempli.

D'après ce que je sais, Symfony2 analyse la configuration et instancie le service l'un après l'autre, puis passe à la gestion du reste de la demande.

Cela signifie que vous ne pouvez pas nécessairement accéder aux différentes parties du conteneur car il peut les charger dans un ordre différent. Vous disposez donc d'un pointeur mémoire vers le conteneur et vous le stockez, mais vous laissez ensuite le framework terminer la construction du conteneur complet avant d'essayer d'accéder à certaines parties de celui-ci. Une exception notable à cette règle est lorsque vous injectez directement le service dans un autre service, auquel cas le conteneur s'assure que ce service est chargé en premier.

Vous pouvez en voir les effets en réalisant deux services. A et B. A reçoit B, et B reçoit A. Vous avez maintenant une référence circulaire. Si, au lieu de cela, vous avez transmis le conteneur à la fois à A et à B, vous ne pouvez pas accéder à A depuis B et à B depuis A sans problème.

1voto

Slava Fomin II Points 1141

Vous devez toujours essayer d'éviter d'injecter un conteneur directement dans vos services.

Je pense que la meilleure solution possible au problème de la "référence circulaire" ainsi qu'aux éventuels problèmes de performance serait d'utiliser " Services paresseux "Disponible à partir de Symfony 2.3.

Il suffit de marquer votre dépendance comme lazy dans la configuration de votre conteneur de services et installer ProxyManager Bridge (voir les détails dans la documentation sur les services paresseux ci-dessus).

J'espère que cela vous aidera, merci.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X