A PHP class can extend only one parent. A trait is a way to share code between classes that are not related by inheritance. Think of it as copy-and-paste handled by PHP.
<?php
trait Timestamps
{
public ?string $createdAt = null;
public function touch(string $time): void
{
$this->createdAt = $time;
}
}
class Post
{
use Timestamps;
}
class Comment
{
use Timestamps;
}
$post = new Post();
$post->touch("2026-01-01");
echo $post->createdAt;
2026-01-01
Both Post and Comment got $createdAt and touch() from the trait, though they have no common parent.
Several traits
<?php
trait Hello
{
public function hello(): string { return "Hello"; }
}
trait World
{
public function world(): string { return "World"; }
}
class Greeter
{
use Hello, World;
public function greet(): string
{
return $this->hello() . " " . $this->world();
}
}
echo (new Greeter())->greet();
Hello World
Conflicts
If two traits have a method with the same name, PHP raises a fatal error unless you choose.
<?php
trait A
{
public function say(): string { return "A"; }
}
trait B
{
public function say(): string { return "B"; }
}
class Talker
{
use A, B {
A::say insteadof B; // use A's version
B::say as sayFromB; // keep B's under another name
}
}
$t = new Talker();
echo $t->say() . $t->sayFromB();
AB
Abstract and static members in traits
A trait can demand that the class provides something.
<?php
trait Describable
{
abstract public function name(): string;
public function describe(): string
{
return "This is " . $this->name();
}
}
class Book
{
use Describable;
public function name(): string
{
return "a book";
}
}
echo (new Book())->describe();
This is a book
A common use: a logging helper
<?php
trait Logs
{
private array $log = [];
protected function log(string $message): void
{
$this->log[] = $message;
}
public function logs(): array
{
return $this->log;
}
}
class Importer
{
use Logs;
public function run(): void
{
$this->log("Started");
$this->log("Finished");
}
}
$i = new Importer();
$i->run();
echo implode(", ", $i->logs());
Started, Finished
Limits of traits
- A trait is not a type. You cannot write a parameter type of a trait, and
instanceofdoes not work with it. Use an interface for that. - Overusing traits makes it hard to see where a method comes from.
- Traits can hide a design problem. If a trait grows large, it is probably a class.
Traits plus interfaces
A good pattern is to define an interface for the type, and a trait that gives a ready implementation.
Why this matters in Laravel
HasFactory, Notifiable, SoftDeletes. Adding use SoftDeletes; to a model gives it a whole set of methods. Now you know what that line does.