Traits
A class can extend only one parent, but sometimes unrelated classes need the same small piece of behaviour — timestamps, a slug generator, logging. A trait is a reusable bundle of methods and properties that's copied into any class that uses it. Laravel and other frameworks use traits heavily.
Defining and using a trait
<?php trait HasTimestamps { public ?string $createdAt = null; public ?string $updatedAt = null; public function touch(string $when): void { $this->createdAt ??= $when; $this->updatedAt = $when; } } class Post { use HasTimestamps; public function __construct(public string $title) {} } class Comment { use HasTimestamps; } $post = new Post("Hello traits"); $post->touch("2026-10-01"); $post->touch("2026-10-08"); echo "{$post->title}: created {$post->createdAt}, updated {$post->updatedAt}\n"; $c = new Comment(); $c->touch("2026-10-05"); echo "Comment updated {$c->updatedAt}\n";
Hello traits: created 2026-10-01, updated 2026-10-08 Comment updated 2026-10-05
Post and Comment share no parent class, yet both got the properties and the touch() method. Think of use inside a class as "paste these members here".
Traits can use the host class
Inside a trait, $this is the object of whatever class uses it. A trait can rely on members the class provides, and can declare abstract methods to make that requirement explicit:
<?php trait Sluggable { abstract public function sluggableText(): string; public function slug(): string { return trim(preg_replace('/[^a-z0-9]+/', '-', strtolower($this->sluggableText())), '-'); } } class Article { use Sluggable; public function __construct(private string $title) {} public function sluggableText(): string { return $this->title; } } class Category { use Sluggable; public function __construct(private string $name) {} public function sluggableText(): string { return $this->name; } } echo new Article("Ten PHP Tips & Tricks!")->slug(), "\n"; echo new Category("Home & Garden")->slug(), "\n";
ten-php-tips-tricks home-garden
Several traits
A class can use several traits. If two traits define a method with the same name, you must decide which one wins with insteadof, and can keep the other under a new name with as:
<?php trait Loggable { public function describe(): string { return "loggable"; } public function log(string $msg): string { return "[log] $msg"; } } trait Exportable { public function describe(): string { return "exportable"; } public function toCsv(array $row): string { return implode(",", $row); } } class Report { use Loggable, Exportable { Exportable::describe insteadof Loggable; Loggable::describe as describeLogging; } } $r = new Report(); echo $r->describe(), " / ", $r->describeLogging(), "\n"; echo $r->log("generated"), " / ", $r->toCsv(["a", 1]), "\n";
exportable / loggable [log] generated / a,1
Static members and constants
Traits can have static properties and methods, and (since PHP 8.2) constants. Each class using the trait gets its own copy of a static property:
<?php trait CountsInstances { private static int $count = 0; public static function created(): int { return self::$count; } private function countInstance(): void { self::$count++; } } class Apple { use CountsInstances; public function __construct() { $this->countInstance(); } } class Pear { use CountsInstances; public function __construct() { $this->countInstance(); } } new Apple(); new Apple(); new Pear(); echo Apple::created(), " apples, ", Pear::created(), " pear\n";
2 apples, 1 pear
Precedence
When names collide, methods defined in the class override the trait's, and the trait's override inherited ones:
<?php class Base { public function hello(): string { return "Base"; } } trait SaysHello { public function hello(): string { return "Trait (overriding " . parent::hello() . ")"; } } class A extends Base { use SaysHello; } class B extends Base { use SaysHello; public function hello(): string { return "Class B"; } } echo new A()->hello(), "\n"; echo new B()->hello(), "\n";
Trait (overriding Base) Class B
When to use traits
Traits are a tool for sharing implementation, not for describing types: you can't type-hint a trait, and instanceof doesn't recognise one. Good uses are small, self-contained behaviours that many classes need. Pair them with an interface when callers need a type:
<?php interface HasSlug { public function slug(): string; } trait SlugFromTitle { public function slug(): string { return strtolower(str_replace(' ', '-', $this->title)); } } class Page implements HasSlug { use SlugFromTitle; public function __construct(public string $title) {} } function linkTo(HasSlug $item): string { return "/" . $item->slug(); } echo linkTo(new Page("About Us")), "\n"; var_dump(class_uses(new Page("x")));
/about-us
array(1) {
["SlugFromTitle"]=>
string(13) "SlugFromTitle"
}Be wary of large traits holding lots of state, or classes using many traits: the behaviour gets scattered, and it becomes hard to see what a class really does. Composition — giving a class an object that does the job — is often clearer.
Try it yourself
Write a HasDiscount trait that adds a discount percentage and a finalPrice() method to any class with a price property.
10.39 0