Трейты в PHP
Трейты
В php нельзя наследовать сразу от нескольких классов сразу. Вместо наследования можно было бы использовать объекты одних классов, внутри других
Однако есть еще один способ, который заключается в использовании трейтов
Трейт - это механизм переиспользования кода в языках с поддержкой одиночного наследования
По другому, трейт - это набор свойств и методов, которые можно включить в другой класс, при этом свойства и методы класса будут восприниматься как свои
Данный механизм позволяет переиспользовать наборы методов в нескольких независимых классах, находящихся в разных иерархиях классов
Трейт похож на класс, но рассчитан только на группировку функциональности тонко контролируемым и согласованным образом
Нельзя создать отдельный экземпляр трейта, трейты предназначены только для подключения к другим классам
Подключение осуществляется с помощью команды use
Трейт - для работы с членами класса трейт не требует наследования
trait TestTrait
{
private a;
private b;
function getReturnA() {
return $this->a;
}
function getReturnB() {
return $this->b;
}
}
class User
{
use TestTrait; #Подключили трейт
public function __construct($a, $b)
{
$this->a = $a;
$this->b = $b;
}
}
$obj = new User(10, 30);
echo $obj->getRerunA();
echo $obj->getReturnB();
#В следующем примере наследуемый из другого класса метод переопределяется с помощью трейта
class Base
{
public function echoHello()
{
echo "Hello";
}
}
trait HelpClass
{
public function echoHello()
{
echo parent::echoHello();
echo " World";
}
}
class Child extends Base
{
use HelpClass;
}
$obj = new Child();
$obj->echoHello();
#Переопределение метода из trait
trait HelpTrait
{
public function hello()
{
echo "Hello World<br>";
}
}
class MyClass
{
use HelpTrait;
#Переопределение метода
public function hello()
{
echo "Hello User";
}
}
$obj = new MyClass();
$obj->hello();
#Пример использования нескольких трейтов
trait Trait1
{
public function echoHello()
{
echo "Hello";
}
}
trait Trait2
{
public function echoWorld()
{
echo " World";
}
}
class MyClass
{
use Trait1;
use Trait2;
public function AddEnd()
{
echo " !";
}
}
$obj1 = new MyClass();
$obj1->echoHello();
$obj1->echoWorld();
$obj1->AddEnd();
Если два трейта добавляют метод с одним и тем же именем, будет вызвана фатальная ошибка
Для разрешения конфликтов таких трейтов, включенных в один и тот же класс, вызывают оператор insteadof, чтобы точно выбрать один из конфликтующих методов
Данный оператор исключает метод, оператор as может включить один из конфликтующих методов под другим имеенем
#Разрешение конфликтов
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}
trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
}
class Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
}
}
class Aliased_Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
B::bigTalk as Talk;
}
}
$obj1 = new Talker();
$obj2 = new Aliased_Talker();
$obj1->smallTalk();
$obj1->bigTalk();
#Вывод bA
echo "<br>";
$obj2->smallTalk();
$obj2->bigTalk();
$obj2->Talk();
#Вывод bAB
Применяя оператор as, можно изменить видимость метода в классе, в который включен трейт
#Изменение области видимости
trait HelloWorld {
public function SayHello() {
echo "Hello World";
}
}
#Изменить видимость метода
class MyClass1 {
use HelloWorld { SayHello as protected; }
public function ChallengeSayHello() {
$this->SayHello();
}
}
#Создать псевдоним метода с измененным модификатором доступа
class MyClass2 {
use HelloWorld { SayHello as private MySayHello; }
public function ChallengeSayHello() {
$this->SayHello();
}
}
$obj1 = new MyClass1();
$obj2 = new MyClass2();
$obj1->ChallengeSayHello();
$obj2->ChallengeSayHello();
#Трейты внутри трейтов
trait A {
public function SayHello() {
echo "Hello";
}
}
trait B {
public function SayWorld() {
echo " World";
}
}
trait C {
use A, B;
public function SaySign() {
echo " !";
}
}
class MyClass {
use C;
public function SayName () {
echo "<br>My name is Egor";
}
}
$obj1 = MyClass();
$obj1->SayHello();
$obj1->SayWorld();
$obj1->SaySign();
$obj1->SayName();
Трейты поддерживают абстрактные методы, чтобы установить требования к классу, в который будет включен трейт
#Использование абстрактных методов
В трейтах можно определить статические переменные, методы, свойства
Начиная с PHP 8.1.0 прямой вызов статического метода или прямой доступ к статическому свойству в трейте устарел. К статическим методам и свойствам нужно обращаться только в классе, в который включён трейт.
Источник: https://www.php.net/manual/ru/language.oop5.traits.php
#Использование статических переменных, свойств, методов и констант
trait Counter {
#Статическое свойство
public static $b = 10;
#Константа
public const TEST = 'test';
public function inc() {
#Статическая переменная
static $c = 0;
$c = $c + 1;
echo "\$c = $c<br>";
#echo "\$b = $b<br>";
}
#Статический метод
public static function ShowAny() {
echo "Bla Bla<br>";
}
}
class C1 {
use Counter;
}
$o = new C1();
$o->inc(); // echo 1
echo $o::$b . "<br>";
$o::ShowAny();
echo C1::$b . "<br>";
C1::ShowAny();
echo C1::TEST . "<br>";
echo $o::TEST . "<br>";
Нельзя изменить значения свойства из трейта, его тип, модификатор доступа
Нельзя определить в классе константу с таким же именем как в трейте
Источник: https://www.php.net/manual/ru/language.oop5.traits.php
Источники
Связанные темы
Оператор разрешения видимости в php
Позднее статическое связывание
Методы создания экземпляра класса в php
Автоматическая загрузка классов
Наследование с помощью extends в php
Конструкторы и деструкторы в php