- Wprowadzone do PHP w wersji 5.4.0.
- krótko mówiąc jest to 'language assisted copy/paste',
- importujemy do klasy za pośrednictwem słowa kluczowego use
- w tej deklaracji możemy importować wiele Trait'ów po przecinku,
- w klasie, miejsce na import Trait'ów jest takie samo jak dla deklarowania pól, stałych i metod - próba importu w ciele metody wyrzuci FATAL ERROR,
- tak naprawdę słowo kluczowe use w przypadku importu Trait'a do klasy, a to wykorzystane do importu klas/interfejsów z innej przestrzeni nazw ma trochę inne zachowanie - ale o tym w innym wpisie,
- nazwę Trait'a można uzyskać w jego metodach dzięki magicznej stałej __TRAIT__ zwracającą jego nazwę wraz z przestrzenią nazw (tak jak został zadeklarowany w use, wewnątrz klasy),
- dostępne mamy słowo kluczowe as które umożliwia zmianę:
- identyfikatora dostępu metody Trait'a,
- nazwę metody Trait'a,
- Trait nie może:
- dziedziczyć po innych Trait'ach,
- implementować interfejsów,
- posiadać const'ów (stałych jak w klasach),
- być instancjonowany,
- Trait może:
- posiadać pola:
- publiczne,
- protected,
- prywatne,
- do takiego pola możemy bez przeszkód odwołać się w metodach klasy która importuje takiego Trait'a,
- statyczne,
- możemy się do nich odwoływać dwojako:
- MyClass::$field
- MyTrait::$field,
- każda klasa która implementuje tego samego Trait'a z polem statycznym, ma jego indywidualną - niezależną kopie,
- Trait i klasa mogą mieć pola o takiej samej nazwie i takiej samej sygnaturze, poniżej niewalidowalne przykłady wyrzucające FATAL ERROR :
- private static $a; - public static $a;
- private static $a; - private $a;
- private static $a = 1; - private static $a = [];
- private static $a; - private static $a = 1;
- private $a; - private $a = 1;
- private $a; - public $a;
- public $a = true; - public $a = false;
- posiadać metody:
- publiczne,
- protected,
- prywatne,
- statyczne, możemy się do nich odwoływać dwojako:
- MyClass::getValue(),
- MyTrait::getValue(),
- abstrakcyjne,
- sygnatura metody w Trait np. public abstract function getValue(int $param): string nie musi być dokładnie taka sama jak w klasie która musi spełnić ten kontrakt, gdzie może wyglądać np. tak: public function getValue(int $param, array $list): string {...} - dokładnie takie same muszą być jedynie nazwy tych metod,
- magiczne (tylko te sprawdziłem):
- __invoke(),
- __toString(),
- __call(),
- odwoływać się do $this,
- używać innych Trait'sów w ten sam sposób co klasy,
- mieć zadeklarowany typ zwracany self oraz TypeHint'ować po nim,
- używać w swoich metodach magicznej stałej __CLASS__
- zwraca wtedy nazwę klasy która go używa,
- chyba że jest to metoda statyczna i odwołujemy się do niej tak: MyTrait::getMagicConstant__CLASS__Value() to wtedy zwraca nazwę Trait'a xD,
- używać ::class na Trait'cie,
- konflikt wystąpi gdy klasa klienta zaimportuje przynajmniej dwa Trait'y gdzie nazwa metody się powtarza,
- Różne sygnatury metod np: getValue(int $option): int {...} oraz getValue(string $option): string {...} nie zmieniają sytuacji,
- pierwszeństwo - gdy klasa i Trait posiada metode getName() - uznana jest metoda klasy,
- precedens - gdy klasa A posiada metodę getName(), ale posiada ją w wyniku dziedziczenia po klasie B oraz (klasa A) importuje Trait'a z metodą getName() - pierwszeństwo ma metoda z Trait'a.
KONFLIKTY NAZW
Gdy klasa korzysta z dwóch Trait'ów które posiadają takie same sygnatury metod. Konflikty nie są rozwiązywane automatycznie i gdy sami się o to nie zatroszczymy to interpreter wywali FATAL ERROR. Konflikty można rozwiązywać za pomocą operatora insteadof - jest to konstrukt językowy, słowo kluczowe charakterystyczne tylko dla Trait'ów np:
use TraitA, TraitB, TraitC {
TraitC::getValue insteadof TraitA, TraitC;
}
------------------------ ------------------------
------------------------ ERROR'sy ------------------------
------------------------ ------------------------
FATAL ERROR - Gdy normalna klasa będzie rozszerzać Traita.
trait Something {}
class Example extends Something {}
//FATAL ERROR Class Example cannot extend from trait Something on line number x
FATAL ERROR - Gdy dodamy do klasy nieistniejącego Traita.
class Example { use UnknownTrait }
//FATAL ERROR Trait 'UnknownTrait ' not found on line number x
FATAL ERROR - Dwa Trait'y będą miały takie same nazwy metod.
trait Something1 {function getName() { return 'Robert'; }}
trait Something2 {function getName() { return 'Stanisław'; }}
class Client { use Something1, Something2; }
//FATAL ERROR Trait method getName has not been applied, because there are collisions with other trait methods on Client on line number x
FATAL ERROR - Gdy dziedziczy po innym Trait'cie.
//FATAL ERROR syntax error, unexpected 'extends' (T_EXTENDS), expecting '{' on line number x
FATAL ERROR - Podczas importu Trait'a do interfejsu (użycia use jak w klasach).
//FATAL ERROR Cannot use traits inside of interfaces. MyTrait1 is used in MyInterface on line number x
FATAL ERROR - Gdy zadeklarujemy const'a wewnątrz Trait'a.
//FATAL ERROR Traits cannot have constants on line number x
FATAL ERROR - Gdy spróbujemy stworzyć instancje Trait'a.
//FATAL ERROR Uncaught Error: Cannot instantiate trait MyTrait
FATAL ERROR - Gdy klasa i Trait będą miały pole o takiej samej nazwie ale innej sygnaturze.
//FATAL ERROR Client and MyTrait define the same property ($a) in the composition of Client. However, the definition differs and is considered incompatible. Class was composed on line number x
Brak komentarzy:
Prześlij komentarz