Трейты - множественное наследование PHP

Категория: / DEV Блог / PHP (LAMP)
trait [treɪt], [treɪ] - характерная черта, особенность, внезапно типаж.

Трейты - механизм повторного использования кода.

Трейты нужны для возможности реализации общего, однотипного функционала для нескольких классов. В PHP<5.4 у разработчика
есть как минимум два варианта реализации подобной задачи - через делегирование или вариант в лоб - наплодить повторяющегося кода, что не есть правильно.

Ребята из PHP реализовали трейты - обрезанное множественное наследование (программный копи-пейст), тем самым избавившись от алмазных проблем.
Трейты похожи на примеси (mixins), за исключением того что для трейтов нельзя напрямую создать экземпляр трейта (инстанцировать).

При использовании множественных трейтов может возникнуть неоднозначность при наличии одноименных методов, на что php
отреагирует фатальной ошибкой (Fatal error: Trait method a has not been applied, because there are collisions with other trait methods). Для уточнения, метод из какого трейта использовать служит оператор insteadof.

<?php
 
trait A {
    function a() { echo "A::a\n";  }
    function b() { echo "A::b\n";  }
}
 
trait B {
    function a() { echo "B::a\n";  }
    function b() { echo "B::b\n";  }
}
 
class Test {
    use A, B {
        B::a insteadof A;
        A::b insteadof B;
    }
}
 
class TestAlias {
    use A, B {
        B::a insteadof A;
        A::b insteadof B;
        B::b as c;
    }
}
 
$t = new Test();
 
$t->a();
$t->b();
 
$ta = new TestAlias();
 
$ta->a();
$ta->b();
$ta->c();


Выведет на экран

B::a
A::b
B::a
A::b
B::b


Вызов $ta->c() отработает метод B::b, по указаному ранее алиасу "c" (синониму) оператором as.

Последним словом о трейтах хотелось бы упомянуть неоднозначное поведение их статических свойств.
Для разных классов использующих общий трейт со статическим свойством, значение этого свойства будет разное!,
в отличии от обычного классного наследования.

trait a { static $s; }
class b { use a; }
class c { use a; }
 
b::$s = 0;
c::$s = 1;
echo b::$s . ' - ' . c::$s;


Выведет: 0 - 1


Трейты, как и классы могут, использоваться в автозагрузчике

spl_autoload_register(function ($name) {
    echo ":: " . $name;
});
 
class A {
    use BTrait;
}
 
$obj = new A();
$obj->hello();


Выведет:

::BTrait

Fatal error: Trait 'BTrait' not found


Другие примеры по трейтам можно посмотреть в Куроводстве PHP.