DEV Community

Cover image for Traits in PHP – a powerful yet underused tool?
Victor
Victor

Posted on

Traits in PHP – a powerful yet underused tool?

πŸ“ 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; } 
Enter fullscreen mode Exit fullscreen mode

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"; } } 
Enter fullscreen mode Exit fullscreen mode

Soft delete

trait SoftDeleteTrait { private ?\DateTime $deletedAt = null; public function delete(): void { $this->deletedAt = new \DateTime(); } public function isDeleted(): bool { return $this->deletedAt !== null; } } 
Enter fullscreen mode Exit fullscreen mode

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 } } 
Enter fullscreen mode Exit fullscreen mode
// Good trait WellDefinedTrait { protected RepositoryInterface $repository; public function setRepository(RepositoryInterface $repo): void { $this->repository = $repo; } public function doSomething(): void { $this->repository->save(); } } 
Enter fullscreen mode Exit fullscreen mode

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; } } 
Enter fullscreen mode Exit fullscreen mode

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)