skip to Main Content

I just came across an unexpected behavior regarding traits.

In a Symfony 6.2 project I use a trait inside a Controller. When calling $this->getUser() or $this->isGranted() it will actually perform correctly and fetch the user or the voter result, just like if it was executed inside the Controller class.

Obviously the code of the trait is executed in the context of the Controller class, so the attributes / methods of the Controller are available inside the trait in this scenario.

Is this intended behavior or just a side effect which I should not rely on?

Pseudo Code:

class SomeController extends AbstractController
{
    use SomeTrait;

    #[Route('/api/some-route', methods:['GET'])]
    public function doSomething(){
        // code
        $this->callTraitMethod();
        // code
    }
}

trait SomeTrait
{
    public function callTraitMethod()
    {
        // code
        $this->getUser(); // works correctly !
        // code
    }
}

2

Answers


  1. The behaviour you observed, where the methods from the trait can access $this->getUser() and $this->isGranted() as if they were called directly from the controller class, is intended behavior. This is because traits are essentially "merged" with the class they are used in, and they share the same scope as the class.

    When you use the SomeTrait in the SomeController class, the methods defined in the trait become part of the controller class, and $this refers to the instance of SomeController, which is why you can access $this->getUser() and $this->isGranted() from within the trait methods without any issues.

    Login or Signup to reply.
  2. Obviously the code of the trait is executed in the context of the Controller class, so the attributes / methods of the Controller are available inside the trait in this scenario.

    Is this intended behavior or just a side effect which I should not rely on?

    This is intended behavior and not a side effect.

    Use (pun intended) the syntax as a mnemonic, the class uses the trait. The class by use also controls overrides.

    So the trait effectively is template code only.

    You could follow the understanding of Barbara Liskov that an abstract (super)class should not contain protected properties at all and transpose that onto traits. While this does not fit 1:1, you could design to only use methods within the traits’s code and never properties (both not defining nor accessing), to design better for abstraction while you write template code.

    "Traits are copy and paste at runtime." (I don’t remember where I heard it first, so if anyone remembers, leave a comment.)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search