PHP only allows single inheritance. In other words, a class can only extend
one other class. But what if you need to include something that doesn't belong in the parent class? Prior to PHP 5.4 you would have to get creative, but in 5.4 Traits were introduced. Traits allow you to basically "copy and paste" a portion of a class into your main class
trait Talk {
/** @var string */
public $phrase = 'Well Wilbur...';
public function speak() {
echo $this->phrase;
}
}
class MrEd extends Horse {
use Talk;
public function __construct() {
$this->speak();
}
public function setPhrase($phrase) {
$this->phrase = $phrase;
}
}
So here we have MrEd
, which is already extending Horse
. But not all horses Talk
, so we have a Trait for that. Let's note what this is doing
First, we define our Trait. We can use it with autoloading and Namespaces (see also Referencing a class or function in a namespace). Then we include it into our MrEd
class with the keyword use
.
You'll note that MrEd
takes to using the Talk
functions and variables without defining them. Remember what we said about copy and paste? These functions and variables are all defined within the class now, as if this class had defined them.
Traits are most closely related to Abstract classes in that you can define variables and functions. You also cannot instantiate a Trait directly (i.e. new Trait()
). Traits cannot force a class to implicitly define a function like an Abstract class or an Interface can. Traits are only for explicit definitions (since you can implement
as many Interfaces as you want, see Interfaces).
The first thing you should do, when considering a Trait, is to ask yourself this important question
Can I avoid using a Trait by restructuring my code?
More often than not, the answer is going to be Yes. Traits are edge cases caused by single inheritance. The temptation to misuse or overuse Traits can be high. But consider that a Trait introduces another source for your code, which means there's another layer of complexity. In the example here, we're only dealing with 3 classes. But Traits mean you can now be dealing with far more than that. For each Trait, your class becomes that much harder to deal with, since you must now go reference each Trait to find out what it defines (and potentially where a collision happened, see Conflict Resolution). Ideally, you should keep as few Traits in your code as possible.