Traits
Sharing method implementations across unrelated classes without inheritance.
2 min read
PHP classes can only extend one parent, but real code often needs to share behavior across classes that don't otherwise belong in the same inheritance chain. Traits solve exactly that.
Defining and using a trait
<?php
trait Loggable {
public function log(string $message): void {
echo "[" . static::class . "] $message\n";
}
}
class Order {
use Loggable;
}
class PaymentProcessor {
use Loggable;
}
$order = new Order();
$order->log("Order created"); // [Order] Order createdA trait looks like a class but can't be instantiated on its own — use Loggable; inside a class copies its methods in, as if they'd been written directly in that class. Order and PaymentProcessor share no inheritance relationship at all, yet both gain log().
Why not just use an abstract base class instead?
<?php
class ApiController {
use Loggable;
}
class BackgroundJob {
use Loggable;
}If ApiController and BackgroundJob each already extend a different, unrelated parent class (as is common in real applications), neither could inherit from a shared Loggable base class too — PHP allows only one extends. A trait sidesteps that limitation entirely, since use isn't inheritance and can be combined freely with extends.
Using multiple traits at once
<?php
trait Timestampable {
public ?string $createdAt = null;
public function markCreated(): void {
$this->createdAt = date("Y-m-d H:i:s");
}
}
class Article {
use Loggable, Timestampable;
}
$article = new Article();
$article->markCreated();
$article->log("Article saved");A class can use several traits in one statement, combining behavior from each. If two traits define a method with the same name, PHP raises a fatal conflict error unless you explicitly resolve it with an insteadof/as block — worth knowing exists, even though it's an edge case you won't hit often.
Traits can have abstract methods too
<?php
trait Comparable {
abstract public function getValue(): int;
public function isGreaterThan(self $other): bool {
return $this->getValue() > $other->getValue();
}
}
class Money {
use Comparable;
public function __construct(private int $cents) {}
public function getValue(): int {
return $this->cents;
}
}A trait can declare an abstract method it doesn't implement, requiring the using class to provide it — here, isGreaterThan() is fully reusable logic that only needs each class to define what "value" means for itself.
When to reach for a trait vs. an interface
An interface says "any class implementing me has these methods" without providing code. A trait actually supplies working method implementations that get copied in. It's common to combine both: define a Comparable interface for the contract, and a ComparableHelpers trait for shared logic that builds on top of it.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.