5

I am working on a Symfony 2.7 WebApp. One of the bundles I created includes a service that offer some user related stuff, e.g. userHasPurchases().

Problem is, that including a Twig Extesion breaks another service:

AppShopService

namespace AppShopBundle\Service;

use AppBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
...

class AppShopService {
    protected $user;

    public function __construct(TokenStorageInterface $tokenStorage, ...) {
        $this->user = $tokenStorage->getToken() ? $tokenStorage->getToken()->getUser() : null;
        ...
    }

    public function userHasPurchases(User $user) {
        $user = $user ? $user : $this->user;
        $result = $user...
        return result;
    }
}

AppShopBundle\Resources\config\services.yml

services:
    app_shop.service:
        class: AppShopBundle\Service\AppShopService
        arguments: 
            - "@security.token_storage"
            - ...

So far everything works fine: The AppShopServices is created with the current user and userHasPurchases() work as expected.

Now I have add a Twig Extension to be able to use userHasPurchases() within my templates:

Twig Extension

namespace AppShopBundle\Twig;

use AppShopBundle\Service\AppShopService;

class AppShopExtension extends \Twig_Extension {
    private $shopService;

    public function __construct(AppShopService $shopService) {
        $this->shopService = $shopService;
    }

    public function getName() {
        return 'app_shop_bundle_extension';
    }

    public function getFunctions() {
        $functions = array();

        $functions[] = new \Twig_SimpleFunction('userHasPurchases', array(
                $this,
                'userHasPurchases'
            ));

        return $functions;
    }

    public function userHasPurchases($user) {
        return $this->shopService->userHasPurchases($user);
    }
}

Including Extension in AppShopBundle\Resources\config\services.yml

services:
    app_shop.service:
        class: AppShopBundle\Service\AppShopService
        arguments: 
            - "@security.token_storage"
            - ...

    app_shop.twig_extension:
        class: AppShopBundle\Twig\AppShopExtension
        arguments: 
          - "@app_shop.service"
        tags:
          - { name: twig.extension }

After icluding the Twig Extension, AppShopService and its method userHasPurchases does not work any more. Problem is, that the constructor of AppShopService does not set user anymore since $tokenStorage->getToken() now returns null.

How is this possible? I have changed nothing except including the Twig Extension. As soon as I remove the Twig Extension from services.yml everything works correctly again.

My only guess is, that the creation fo the Twig Extension is done before any security. But why?

Any idea what might be wrong here?

Andrei Herford
  • 15,149
  • 16
  • 73
  • 171
  • 1
    In general, you don't want to get the user in the constructor. You never really know if the security component has done it's thing before your object is created. So just add a MyService::getUser and call it whenever it is needed. Save yourself some timing headaches. – Cerad Apr 21 '16 at 12:46
  • 1
    don't interact with the tokenStorage in the constructor but only in the `userHasPurchases`` method – Matteo Apr 21 '16 at 12:48

1 Answers1

3

don't interact with the tokenStorage in the constructor but only in the userHasPurchases method.

namespace AppShopBundle\Service;

use AppBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
...

class AppShopService {
    protected $tokenStorage;

    public function __construct(TokenStorageInterface $tokenStorage, ...) {
        $this->tokenStorage = $tokenStorage;
    }

    public function userHasPurchases(User $user) {
        $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
        $result = $user...
        return result;
    }
}

Hope this help

Andrei Herford
  • 15,149
  • 16
  • 73
  • 171
Matteo
  • 32,801
  • 10
  • 89
  • 100
  • Thanks a lot! This indeed solves the problem. But why? I understand that timing can be an issue, but is security/templating/etc done async? I use `$this->user =$this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;` in several other services as well, without any problem so far. What is so special in this case? – Andrei Herford Apr 21 '16 at 13:20
  • hi @AndreiHerford it depends of the lifecycle of the service but i don't find any resources about it at the moment. Usually I interact with the service only when i need it, and never in the constructor of the service itself so the service can be stateless (avoid mutable call attribute). Hope this help – Matteo Apr 21 '16 at 13:44