You're basically asking two questions here.
- What are interfaces and why are they useful?
- What are traits and why are they useful?
To understand why interfaces are useful you have to know a little about inheritance and OOP in general. If you've ever heard the term spaghetti code before (it's when you tend to write imperative code that's so tangled together you can hardly make sense of it) then you should liken that to the term lasagna code for OOP (that's when you extend a class to so many layers that it becomes difficult to understand which layer is doing what).
1. Interfaces
Interfaces diffuse some of this confusion by allow a class to implement a common set of methods without having to restrict the hierarchy of that class. we do not derive interfaces from a base class. We merely implement them into a given class.
A very clear and obvious example of that in PHP is DateTimeInterface
. It provides a common set of methods which both DateTime
and DateTimeImmutable
will implement. It does not, however, tell those classes what the implementation is. A class is an implementation. An interface is just methods of a class sans implementation. However, since both things implement the same interface it's easy to test any class that implements that interface, since you know they will always have the same methods. So I know that both DateTime
and DateTimeImmutable
will implement the method format
, which will accept a String
as input and return a String
, regardless of which class is implementing it. I could even write my own implementation of DateTime
that implements DateTimeInterface
and it is guaranteed to have that method with that same signature.
So imagine I wrote a method that accepts a DateTime
object, and the method expects to run the format
method on that object. If it doesn't care which class, specifically, is given to it, then that method could simply typehint its prototype as DateTimeInterface
instead. Now anyone is free to implement DateTimeInterface
in their own class, without having to extend from some base class, and provide my method with an object that's guaranteed to work the same way.
So in relation to your EventEmitter
example, you can add the same capabilities of a class (like DateTime
) to any class that might not even extend from DateTime
, but as long as we know it implements the same interface, we know for sure it has the same methods with the same signatures. This would mean the same thing for EventEmitter
.
2. Traits
Traits, unlike interfaces, actually can provide an implementation. They are also a form of horizontal inheritance, unlike the vertical inheritance of extending classes. Because two completely different class that do not derive from the same base class can use the same Trait
. This is possible, because in PHP traits are basically just compiler-assisted copy and paste. Imagine, you literally copied the code inside of a trait and just pasted it into each class that uses it right before compile time. You'd get the same result. You're just injecting code into unrelated classes.
This is useful, because sometimes you have a method or set of methods that prove reusable in two distinct classes even though the rest of those classes have nothing else in common.
For example, imagine you are writing a CMS, where there is a Document
class and a User
class. Neither of these two classes are related in any meaningful way. They do very different things and it makes no sense for one of them to extend the other. However, they both share a particular behavior in common: flag() method that indicates the object has been flagged by a user for purposes of violating the Terms of Service.
trait FlagContent {
public function flag(Int $userId, String $reason): bool {
$this->flagged = true;
$this->byUserId = $userId;
$this->flagReason = $reason;
return $this->updateDatabase();
}
}
Now consider that perhaps your CMS has other content that's subject to being flagged, like a Image
class, or a Video
class, or even a Comment
class. These classes are all typically unrelated. It probably wouldn't make much sense just to have a specific class for flagging content, especially if the properties of the relevant objects have to be passed around to this class to update the database, for example. It also doesn't make sense for them to derive from a base class (they're all completely unrelated to each other). It also doesn't make sense to rewrite this same code in every class, since it would easier to change it in one place instead of many.
So what seems to be most sensible here is to use a Trait
.
So again, in relation to your EventEmitter
example, they're giving you some traits you can reuse in your implementing class to basically make it easier to reuse the code without having to extend from a base class (horizontal inheritance).