Traits have been one of the biggest additions for PHP 5.4. I know the syntax and understand the idea behind traits, like horizontal code re-use for common stuff like logging, security, caching etc.
However, I still don't know how I would make use of traits in my projects.
Are there any open source projects that already use traits? Any good articles/reading material on how to structure architectures using traits?
I guess one would have to look into languages that have Traits for some time now to learn the accepted Good/Best practices. My current opinion on Trait is that you should only use them for code that you would have to duplicate in other classes that share the same functionality.
Example for a Logger trait:
interface Logger
{
public function log($message, $level);
}
class DemoLogger implements Logger
{
public function log($message, $level)
{
echo "Logged message: $message with level $level", PHP_EOL;
}
}
trait Loggable // implements Logger
{
protected $logger;
public function setLogger(Logger $logger)
{
$this->logger = $logger;
}
public function log($message, $level)
{
$this->logger->log($message, $level);
}
}
class Foo implements Logger
{
use Loggable;
}
And then you do (demo)
$foo = new Foo;
$foo->setLogger(new DemoLogger);
$foo->log('It works', 1);
I guess the important thing to consider when using traits is that they really are just pieces of code that get copied into the class. This can easily lead to conflicts, for instance, when you try to change visibility of methods, e.g.
trait T {
protected function foo() {}
}
class A {
public function foo() {}
}
class B extends A
{
use T;
}
The above will result in an error (demo). Likewise, any methods declared in the trait that are also already declared in the using class will not get copied into the class, e.g.
trait T {
public function foo() {
return 1;
}
}
class A {
use T;
public function foo() {
return 2;
}
}
$a = new A;
echo $a->foo();
will print 2 (demo). These are things you will want to avoid because they make errors hard to find. You will also want to avoid putting things into traits that operate on properties or methods of the class that uses it, e.g.
class A
{
use T;
protected $prop = 1;
protected function getProp() {
return $this->prop;
}
}
trait T
{
public function foo()
{
return $this->getProp();
}
}
$a = new A;
echo $a->foo();
works (demo) but now the trait is intimately coupled to A and the whole idea of horizontal reuse is lost.
When you follow the Interface Segregation Principle you will have many small classes and interfaces. That makes Traits an ideal candidate for the things you mentioned, e.g. crosscutting concerns, but not to compose objects (in a structual sense). In our Logger example above, the trait is completely isolated. It has no dependencies on concrete classes.
We could use aggregation/composition (like shown elsewhere on this page) to achieve the same resulting class, but the drawback of using aggregation/composition is that we will have to add the proxy/delegator methods manually to each and every class then that should be able to log. Traits solve this nicely by allowing me to keep the boilerplate in one place and selectively apply it where needed.
Note: given that traits are a new concept in PHP, all opinion expressed above is subject to change. I've not have had much time to evaluate the concept myself yet. But I hope it is good enough to give you something to think about.