6

This is a follow-up to my previous question about resolving the diamond issue in php.

As I state in that question, I resolve my problem by using traits and passing the instance of the class to the method of the trait. Such as:

trait SecurityTrait
{
    public function beforeExecuteRouteTrait($controller, Dispatcher $dispatcher)
    {
        // Do something that makes use of methods/members of the controller
    }
}

class AppController extends Controller
{
    use SecurityTrait;

    public function beforeExecuteRoute(Dispatcher $dispatcher)
    {
        return $this->beforeExecuteRouteTrait($this, $dispatcher);
    }
}

However, I am still uncomfortable with this as I don't think this is how traits are really supposed to be used. In my reading I haven't found any way in which to access class members in traits (make $this inside a trait refer to the class using it). Is this possible? Or is there another way to implement a similar behaviour?

After reading some of the answers...

Previously I thought I had received errors when using $this->... inside the trait and this led me to believe the trait could not access anything to do with the underlying class. After reading the answers I tried altering my code to use $this->... inside a trait again and it works - which means a typo several weeks ago has given me far too much headache...

The example given previously now looks like this

trait SecurityTrait
{
    public function beforeExecuteRoute(Dispatcher $dispatcher)
    {
        // Do something that makes use of methods/members of the controller
    }
}

class AppController extends Controller
{
    use SecurityTrait;
}

Much cleaner and more easily understandable but provides the same functionality.

Community
  • 1
  • 1
Kvothe
  • 1,819
  • 2
  • 23
  • 37

2 Answers2

15

If you use a trait inside a class then that trait has full access to all class's members and vice versa - you can call private trait methods from the class itself.

Think of traits as code that literally gets copy/pasted into the class body.

For example:

trait Helper
{
    public function getName()
    {
        return $this->name;
    }

    private function getClassName()
    {
        return get_class($this);
    }
}

class Example
{
    use Helper;

    private $name = 'example';

    public function callPrivateMethod()
    {
        // call a private method on a trait
        return $this->getClassName();
    }
}

$e = new Example();
print $e->getName(); // results in "example"
print $e->callPrivateMethod(); // results in "Example"

In my view referencing classes in traits is not the best way to use them but there's nothing stopping anyone from doing it.

Andris
  • 5,853
  • 3
  • 28
  • 34
  • I didn't think it was the best way to use them either but couldn't think of an alternative that it is still succinct to solve my issue. If you have an alternative suggestion I'd love to hear it – Kvothe Sep 02 '15 at 22:37
  • Having read your diamond question I think composition would be the most appropriate solution (which is essentially dependency injection as your accepted answer suggests). If you have two classes that extend the same base class but one needs to do something the other doesn't then you can inject the utility classes into your controllers as dependencies. Constructor of your base controller could require some classes to be injected into it that you can reuse, or alternatively, you could have a service manager like ZF2 does that you use from the controllers. Hard to say not knowing the context. – Andris Sep 02 '15 at 22:45
  • Thanks. I'm going to leave it as is for the time being so I can get the later parts running properly. Then I can come back to this at my leisure and properly set up a composition / dependency injection solution (having seen the power of DI in the framework I'm using I think you're definitely right about it likely being the best solution). – Kvothe Sep 02 '15 at 22:54
  • Thanks for accepted answer! Just curious - what framework are you using? – Andris Sep 03 '15 at 08:29
  • It's [PhalconPHP](https://phalconphp.com/en/). A highly decoupled MVC framework that is actually written in C - you install it as an extension. – Kvothe Sep 03 '15 at 08:55
  • Oh yeah, we looked into using it at my previous company as it is very similar to the framework they used, Slim, but eventually decided against it because of potential problems debugging C code if something went wrong. It looks nice though. – Andris Sep 03 '15 at 09:06
  • That was a concern here too, but the introduction of [Zephir](http://zephir-lang.com/) should hopefully lessen that (if we stick with it - I discovered it recently and am now demoing its capabilities to the others on my team). – Kvothe Sep 03 '15 at 09:15
3

No, that's exactly what Traits are for. Your class already extends a class so you can't inherit the methods and variables of any other classes.

Think of a Trait like copy/paste for code execution. When a class includes a Trait, it's just as if you had written all that code into the class itself.

Machavity
  • 30,841
  • 27
  • 92
  • 100
  • Thanks, that's good to know. I was concerned about bad practices like [this answer](http://codereview.stackexchange.com/questions/74077/trait-accessing-variables-of-classes-using-it?answertab=votes#tab-top) highlights. – Kvothe Sep 02 '15 at 22:36