Traits
A class can have only one parent. But sometimes the same code is needed by classes that have nothing in common: both a post and a product could use a method that builds a page address. Traits exist for that — sets of methods that can be attached to any class.
Declaring and using
A trait is declared with the word trait and attached inside a class with the word use:
<?php
trait Greets
{
public function greet(): string
{
return 'Hello!';
}
}
class User
{
use Greets;
}
echo (new User())->greet(); // Hello!A trait's method works as if it were written in the class itself.
One trait, many classes
<?php
trait HasSlug
{
public function slug(): string
{
return strtolower(str_replace(' ', '-', $this->title));
}
}
class Post
{
use HasSlug;
public function __construct(public string $title) {}
}
class Product
{
use HasSlug;
public function __construct(public string $title) {}
}
echo (new Post('Hello World'))->slug(), "\n"; // hello-world
echo (new Product('Green Tea'))->slug(); // green-teaThe trait reads $this->title — a property of the class it is attached to.
Properties in a trait
A trait can declare properties as well:
<?php
trait CountsViews
{
private int $views = 0;
public function view(): int
{
return ++$this->views;
}
}
class Page
{
use CountsViews;
}
$page = new Page();
$page->view();
echo $page->view(); // 2A requirement for the class
An abstract method in a trait obliges the class to implement it. This is how a trait states what it needs to work:
<?php
trait Describes
{
abstract public function name(): string;
public function describe(): string
{
return 'This is ' . $this->name();
}
}
class City
{
use Describes;
public function name(): string
{
return 'London';
}
}
echo (new City())->describe(); // This is LondonSeveral traits
Traits are listed with commas. If they have methods with the same name, the conflict is resolved with the word insteadof:
<?php
trait Hello
{
public function say(): string
{
return 'Hello';
}
}
trait Bye
{
public function say(): string
{
return 'Bye';
}
}
class Speaker
{
use Hello, Bye {
Hello::say insteadof Bye;
Bye::say as sayBye;
}
}
$speaker = new Speaker();
echo $speaker->say(), ' ', $speaker->sayBye(); // Hello ByeA trait or inheritance
| Situation | What to choose |
|---|---|
| The class is a kind of another class | Inheritance |
| Unrelated classes need the same behavior | A trait |
| The presence of methods must be guaranteed | An interface |
Common mistake
A trait is not a type. You cannot check with instanceof whether it is attached. If objects have to be told apart by that, add an interface:
<?php
interface Sluggable
{
public function slug(): string;
}
trait HasSlug
{
public function slug(): string
{
return 'slug';
}
}
class Post implements Sluggable
{
use HasSlug;
}
var_dump(new Post() instanceof Sluggable); // bool(true)