π This article is an English translation of the original French version available here: https://victor-prdh.com/blog/02-traits-avec-php/
PHP does not support multiple inheritance. To work around this limitation, Traits were introduced in PHP 5.4. They allow injecting methods into several classes without using class inheritance.
When misused, they can harm readability and maintainability. When mastered, they are a great tool to structure shared code. Here's an overview of use cases, best practices, and common pitfalls.
1. Why Traits?
In PHP, a class can only inherit from one parent class. Traits allow factoring reusable behavior into multiple different classes.
trait LoggerTrait { public function log(string $message): void { echo "[LOG] $message\n"; } } class ServiceA { use LoggerTrait; } class ServiceB { use LoggerTrait; }
2. Practical use cases
Traits are useful for encapsulating cross-cutting behaviors.
Shared logger
trait LoggerTrait { public function log(string $message): void { echo date('Y-m-d H:i:s') . " - $message\n"; } }
Soft delete
trait SoftDeleteTrait { private ?\DateTime $deletedAt = null; public function delete(): void { $this->deletedAt = new \DateTime(); } public function isDeleted(): bool { return $this->deletedAt !== null; } }
3. Best practices
One Trait = one responsibility
Avoid catch-all Traits. Each Trait should have a clear and focused purpose.
Explicit method names
Clear names help avoid collisions. Prefer handleForm()
over handle()
.
No hidden dependencies
A Trait should not assume the existence of properties or methods unless clearly defined or injected.
// Bad trait BrokenTrait { public function doSomething(): void { $this->repository->save(); // hidden dependency } }
// Good trait WellDefinedTrait { protected RepositoryInterface $repository; public function setRepository(RepositoryInterface $repo): void { $this->repository = $repo; } public function doSomething(): void { $this->repository->save(); } }
4. Pitfalls to avoid
Method name collisions
If two Traits define the same method, you must resolve the conflict explicitly.
class C { use A, B { B::sayHello insteadof A; } }
Overuse
Too many Traits in a single class can reduce clarity.
False abstraction
Traits are not a replacement for solid object-oriented design. If you need polymorphism or hierarchy, prefer interfaces or abstract classes.
5. Alternatives
- Interface + abstract class: more restrictive but often clearer.
- Composition: delegate responsibilities to other objects.
- Dedicated services: in Symfony, injecting a service is often cleaner.
Conclusion
Traits are a useful tool to structure shared codeβif used with moderation and discipline.
In short:
- Keep them focused.
- Avoid hidden dependencies.
- Prefer composition when Traits become too heavy.
Well used, Traits simplify. Misused, they complicate. It's your call.
What about you?
Were you familiar with Traits in PHP?
Share your experience or tips with me on LinkedIn β happy to chat!
Top comments (0)