Трейты
У класса может быть только один родитель. Но бывает, что одинаковый код нужен классам, которые ничем не связаны: и статье, и товару пригодится метод для формирования адреса страницы. Для этого есть трейты — наборы методов, которые можно подключить к любому классу.
Объявление и подключение
Трейт объявляют словом trait, а подключают внутри класса словом use:
<?php
trait Greets
{
public function greet(): string
{
return 'Привет!';
}
}
class User
{
use Greets;
}
echo (new User())->greet(); // Привет!Метод трейта работает так, будто написан в самом классе.
Один трейт — много классов
<?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Трейт обращается к $this->title — свойству класса, в который он подключён.
Свойства в трейте
В трейте можно объявлять и свойства:
<?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Требование к классу
Абстрактный метод в трейте обязывает класс реализовать его. Так трейт заявляет, что ему нужно для работы:
<?php
trait Describes
{
abstract public function name(): string;
public function describe(): string
{
return 'Это ' . $this->name();
}
}
class City
{
use Describes;
public function name(): string
{
return 'Казань';
}
}
echo (new City())->describe(); // Это КазаньНесколько трейтов
Трейты перечисляют через запятую. Если в них есть методы с одинаковым именем, конфликт разрешают словом insteadof:
<?php
trait Hello
{
public function say(): string
{
return 'Привет';
}
}
trait Bye
{
public function say(): string
{
return 'Пока';
}
}
class Speaker
{
use Hello, Bye {
Hello::say insteadof Bye;
Bye::say as sayBye;
}
}
$speaker = new Speaker();
echo $speaker->say(), ' ', $speaker->sayBye(); // Привет ПокаТрейт или наследование
| Ситуация | Что выбрать |
|---|---|
| Класс — разновидность другого класса | Наследование |
| Одинаковое поведение нужно несвязанным классам | Трейт |
| Нужно гарантировать наличие методов | Интерфейс |
Частая ошибка
Трейт — не тип. Проверить через instanceof, подключён ли он, нельзя. Если по этому признаку нужно отличать объекты, добавьте интерфейс:
<?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)