Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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-tea

The 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(); // 2

A 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 London

Several 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 Bye

A trait or inheritance

SituationWhat to choose
The class is a kind of another classInheritance
Unrelated classes need the same behaviorA trait
The presence of methods must be guaranteedAn 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)

Thanks for reading!

Donate to the site — we work hard for you.