Трейты в PHP
Как мы знаем, в PHP класс может наследоваться только от одного класса. Но как быть, если мы хотим иметь какой-либо функционал в разных классах, которые не являются наследниками друг друга? Для этого придумали трейты. Трейты в PHP – это такой механизм, который позволяет внутри классов избегать повторного использования кода.
Давайте разберём на примере. Пусть у нас будут два совершенно не связанных между собой класса: коробка и человек. Но предположим, что мы хотели бы, чтобы они оба умели говорить о том, какого они класса. Давайте для начала создадим два этих класса и прямо в них создадим методы sayYourClass(), которые будут выводить имя класса, объектами которого они являются.
<?php
class Man
{
public function sayYourClass(): string
{
return 'My class is Man';
}
}
class Box
{
public function sayYourClass(): string
{
return 'My class is Box';
}
}
$man = new Man();
$box = new Box();
echo $man->sayYourClass();
echo $box->sayYourClass();
Результат:
My class is Man
My class is Box
В PHP можно получить имя класса с помощью конструкции ИмяКласса::class. Например:
echo Box::class;
Выведет:
Box
Если мы находимся внутри класса, например, в каком-то его методе, то мы можем ИмяКласса заменить словом self – текущий класс.
<?php
class Man
{
public function sayYourClass(): string
{
return 'My class is ' . self::class;
}
}
class Box
{
public function sayYourClass(): string
{
return 'My class is ' . self::class;
}
}
$man = new Man();
$box = new Box();
echo $man->sayYourClass();
echo $box->sayYourClass();
Результат останется прежним.
My class is Man
My class is Box
Как видим, в классах получились одинаковые методы, и было бы неплохо избавиться от дублирования. Вот тут нам и понадобится трейт. Описываются трейты следующим образом:
trait SayYourClassTrait
{
public function sayYourClass(): string
{
return 'My class is ' . self::class;
}
}
Ничего сложного. Теперь мы можем просто использовать этот трейт в двух наших классах. Для этого используется конструкция use.
class Man
{
use SayYourClassTrait;
}
class Box
{
use SayYourClassTrait;
}
$man = new Man();
$box = new Box();
echo $man->sayYourClass();
echo $box->sayYourClass();
И снова увидим нужный результат:
My class is Man
My class is Box
Код из трейта SayYourClass просто подставился в классы, где мы его использовали с помощью слова use. В self будет лежать класс, в котором сейчас исполняется этот код. Вот так всё просто.
Трейты также довольно плотно пересекаются с темой интерфейсов.
Давайте добавим интерфейс, который будет обязывать классы иметь метод sayYourClass().
interface ISayYourClass
{
public function sayYourClass(): string;
}
Теперь наши классы могут его реализовать – этот метод реализован в трейте, а трейт используется в классе.
Давайте посмотрим на получившийся код.
interface ISayYourClass
{
public function sayYourClass(): string;
}
trait SayYourClassTrait
{
public function sayYourClass(): string
{
return 'My class is ' . self::class;
}
}
class Man implements ISayYourClass
{
use SayYourClassTrait;
}
class Box implements ISayYourClass
{
use SayYourClassTrait;
}
$man = new Man();
$box = new Box();
echo $man->sayYourClass();
echo $box->sayYourClass();
В PHP класс может наследоваться только от одного класса, помните? Так вот с помощью интерфейсов и трейтов мы можем это ограничение немного обойти. Мы теперь можем добавлять некоторый функционал в классы, которые не имеют какого-то общего поведения в целом. Но они при этом объединены одним интерфейсом. А проверять, реализует ли объект интерфейс, мы уже умеем – с помощью конструкции instanceof.
Таким образом, мы можем обходить ограничения наследования:
- класс реализует что-то, и мы можем быть в этом уверены (проверив на реализацию интерфейса);
- класс может иметь несколько таких независимых друг от друга реализаций (благодаря возможности реализовать несколько интерфейсов и подключить несколько трейтов).
Комментарии