Traits
PHP classes can only extend one parent class. That single-inheritance rule keeps class hierarchies easy to reason about, but it leaves a real gap: what do you do when two unrelated classes need the exact same chunk of behavior, and neither one is a natural parent of the other? Traits are PHP's answer. A trait is a named block of methods (and properties) that you can pull into any class with a use statement, as if you had copy-pasted its contents directly into the class body. It is code reuse without inheritance — sometimes called "horizontal" reuse, because instead of climbing up a parent/child chain, you are pasting the same slab of code sideways into however many classes need it.
Declaring and using a trait
A trait looks like a class but is declared with the trait keyword and can never be instantiated on its own — new SomeTrait() is a fatal error. A class pulls it in with use TraitName; inside the class body.
A logging trait shared by two unrelated classes
<?php
trait Loggable {
protected array $logs = [];
public function log(string $message): void {
$this->logs[] = $message;
}
public function getLogs(): array {
return $this->logs;
}
}
class Order {
use Loggable;
public function ship(): void {
$this->log('Order shipped');
}
}
class Invoice {
use Loggable;
public function send(): void {
$this->log('Invoice sent');
}
}
$order = new Order();
$order->ship();
$invoice = new Invoice();
$invoice->send();
print_r($order->getLogs());
print_r($invoice->getLogs());Array
(
[0] => Order shipped
)
Array
(
[0] => Invoice sent
)Order and Invoice share no common ancestor besides the built-in
base of every class, yet both got a fully working log() method and
their own private $logs array. Each class that uses Loggable gets
its own independent copy of the trait's properties — $order->logs
and $invoice->logs do not interfere with each other.
Using multiple traits at once
A class can pull in several traits in a single use statement, separated by commas. This is where traits earn their keep: you can assemble a class out of small, independently testable behavior blocks instead of writing one big parent class that tries to cover every combination.
Combining two traits in one class
<?php
trait Timestampable {
public function touch(): void {
echo "Updated at: " . date('Y-m-d') . "\n";
}
}
trait Loggable {
public function log(string $message): void {
echo "LOG: {$message}\n";
}
}
class Article {
use Timestampable, Loggable;
}
$article = new Article();
$article->touch();
$article->log('Article saved');Updated at: 2026-07-02 LOG: Article saved
Resolving conflicts: insteadof and as
Two traits used in the same class might define a method with the same name. PHP will not silently pick one — it throws a fatal error unless you resolve the collision explicitly with an insteadof clause, and optionally give the loser an alias with as so you can still reach it under a different name.
Two traits both define greet(); resolve which one wins
<?php
trait English {
public function greet(): string {
return 'Hello';
}
}
trait Spanish {
public function greet(): string {
return 'Hola';
}
}
class Greeter {
use English, Spanish {
English::greet insteadof Spanish;
Spanish::greet as greetInSpanish;
}
}
$greeter = new Greeter();
echo $greeter->greet(), "\n";
echo $greeter->greetInSpanish(), "\n";Hello Hola
English::greet insteadof Spanish tells PHP which implementation becomes the class's actual greet() method. The as line does not create a second, competing method — it just gives the shadowed Spanish::greet a new local name, greetInSpanish(), so nothing is lost. as can also be used purely to change visibility, e.g. English::greet as protected;, without renaming anything.
Abstract and static members in traits
Traits can declare abstract methods that force the using class to provide an implementation, and they can hold their own static properties and methods, each independent per class that uses them.
A trait that demands an implementation from its user
<?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 = new Money(500);
$b = new Money(300);
var_dump($a->isGreaterThan($b));bool(true)
Traits vs interfaces vs abstract classes
These three tools solve related but distinct problems, and mixing them up is one of the more common design mistakes in PHP codebases:
Interface — a contract only. It says what a class can do (method signatures) but supplies zero implementation. Use it when unrelated classes need to be treated the same way through a common type, e.g. anything
CountableorJsonSerializable.Abstract class — a partial implementation plus a contract, but a class can extend only one. Use it when you have a real "is-a" relationship and want to share both state and behavior down a single inheritance line.
Trait — implementation only, no type. A trait does not satisfy
instanceofchecks and cannot be used as a type hint. Use it purely to share behavior across classes that have no "is-a" relationship at all.
A practical pattern is to combine them: define an interface for the public contract, then use one or more traits to provide a default implementation of that contract, and let classes implements the interface while pulling in the trait for the boilerplate.