Notes on PHP: Traits, Interfaces, and Class Composition
Today I learned a few PHP features that I had never really thought about before. Thanks to Laravel, most of these things are already abstracted away, so I rarely think about PHP deeply as a programming language itself.
1. Interface
Just like in other languages, an interface is commonly used as a contract for a class that can have multiple implementations.
For example, an UserRepository interface can be implemented using MySQL or PostgreSQL.
This allows both implementations to share the same method signatures, so any consumer of the interface doesnât need to change anything when the underlying implementation changes.
Example:
1interface UserRepository
2{
3 public function set($params): string;
4 public function get(): string;
5}
6
7class MysqlUserRepository implements UserRepository
8{
9 public function set($params): string
10 {
11 // MySQL implementation
12 }
13
14 public function get(): string
15 {
16 // implementation
17 }
18}
19
20class PgUserRepository implements UserRepository
21{
22 public function set($params): string
23 {
24 // PostgreSQL implementation
25 }
26
27 public function get(): string
28 {
29 // implementation
30 }
31}Both classes implement the same interface, so any code that depends on UserRepository can use its methods without knowing the concrete implementation.
2. Trait
A trait is basically a collection of methods that can be composed into multiple classes. Itâs usually used to share common functionality across classes without using inheritance.
Example:
1trait HasSlug
2{
3 public function generateSlug(string $title): string
4 {
5 return strtolower(str_replace(' ', '-', $title));
6 }
7}
8
9class Blog
10{
11 use HasSlug; // Blog automatically has generateSlug()
12}3. Favor Composition Over Inheritance
In general, we are encouraged to use composition (for example via dependency injection) instead of inheritance. Composition is more flexible, easier to test, and less tightly coupled.
Example:
1class User
2{
3 protected $name;
4
5 public function name(): string
6 {
7 return $this->name;
8 }
9}
10
11class Blog
12{
13 public function __construct(public User $user) {}
14
15 public function name(): string
16 {
17 return $this->user->name();
18 }
19}In this example, we can access User.name without using inheritance.
This becomes even more powerful if the constructor accepts an interface instead of a concrete class,
because it makes it easier to swap implementations later.
PHP used to have a bad reputation â messy and painful to work with. And honestly, that wasnât completely wrong back then.
But after understanding these âmodern languageâ features, I realized PHP can actually be clean and elegant. Laravel hides a lot of complexity, but knowing the fundamentals makes coding much more enjoyable.