Menu

Traity w PHP: use, kilka traitów i insteadof

Trait w PHP to wielokrotnego użytku blok metod i właściwości, który kopiujesz do klasy przez use TraitName;. Poznaj używanie kilku traitów, rozwiązywanie konfliktów nazw przez insteadof i as, abstrakcyjne i statyczne elementy traitów, kolejność pierwszeństwa metod oraz trait vs interfejs vs klasa abstrakcyjna.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Trait w PHP to wielokrotnego użytku blok metod i właściwości. Deklarujesz go przez trait Name { ... } i dołączasz do dowolnej klasy przez use Name; w treści klasy. PHP kopiuje elementy traitu do klasy, więc działają dokładnie tak, jakbyś napisał je w niej.

User i Robot nie mają wspólnego rodzica, a mimo to obie mają teraz hello(). Traity istnieją właśnie na taki przypadek: klasy w PHP mogą rozszerzać tylko jedną klasę, a trait współdzieli kod w bok, między klasami, które nie są spokrewnione.

Traity z właściwościami

Trait może przynieść właściwości razem z metodami. Metody używają $this jak każda metoda klasy, bo po skopiowaniu traitu są metodami klasy:

Klasa nie może zadeklarować właściwości o tej samej nazwie co właściwość traitu, chyba że obie deklaracje są identyczne (ta sama widoczność, typ i wartość domyślna); w przeciwnym razie to błąd krytyczny.

Używanie kilku traitów

Wymień kilka traitów w jednej instrukcji use, oddzielając je przecinkami, albo napisz kilka linii use. Klasa dostaje elementy ich wszystkich:

Rozwiązywanie konfliktów przez insteadof i as

Gdy dwa traity definiują metodę o tej samej nazwie, PHP nie zgaduje i zatrzymuje się z błędem krytycznym. W bloku use insteadof wybiera zwycięzcę, a as zachowuje przegraną metodę pod inną nazwą:

Ze zwykłym use EnglishGreeting, FrenchGreeting; bez bloku skrypt zatrzymuje się, zanim cokolwiek zostanie wypisane:

PHP Fatal error:  Trait method FrenchGreeting::greet has not been applied as Host::greet, because of collision with EnglishGreeting::greet in /home/index.php on line 12

as może też zmienić widoczność. use Loggable { log as protected; } czyni metodę log() traitu chronioną tylko w tej klasie, a use Loggable { log as private writeLog; } dodaje prywatny alias.

Elementy abstrakcyjne i statyczne w traicie

Trait może czegoś wymagać od klasy, która go używa, deklarując metodę abstrakcyjną. Może też mieć metody i właściwości statyczne. Każda klasa używająca traitu dostaje własną kopię właściwości statycznej:

Car i Bike liczą osobno, bo właściwość statyczna jest kopiowana do każdej klasy. To co innego niż właściwość statyczna zadeklarowana we wspólnej klasie rodzica, którą współdzielą wszyscy potomkowie; ten przypadek pokazuje strona o static.

Która metoda wygrywa: klasa, trait czy rodzic

Jeśli ta sama nazwa metody istnieje w samej klasie, w używanym przez nią traicie i w jej klasie rodzica, PHP wybiera w tej kolejności: własna metoda klasy, potem traitu, potem rodzica. Wewnątrz traitu parent:: nadal sięga do wersji rodzica:

Trait nie jest typem

instanceof nigdy nie pasuje do traitu, a parametr z typem będącym nazwą traitu odrzuca każdy obiekt z TypeError. Aby zapytać „czy ta klasa ma to zachowanie”, połącz trait z interfejsem albo sprawdź klasę przez class_uses():

Powyższy wzorzec jest typowy: interfejs Exportable to to, czego używają funkcje jako typu i co sprawdza instanceof, a trait ExportsProperties to gotowa implementacja, której klasy mogą użyć albo ją zastąpić. Sam trait to współdzielony kod bez kontraktu; gdy zauważysz, że sprawdzasz class_uses() w logice biznesowej, trait potrzebował obok siebie interfejsu. Dla kodu współdzielonego przez rodzinę spokrewnionych klas prostszym wyborem może być klasa abstrakcyjna.

Najczęściej zadawane pytania

Czym jest trait w PHP?

Trait to nazwana grupa metod (i właściwości) zadeklarowana przez trait Name { }, którą klasa dołącza przez use Name;. PHP kopiuje elementy traitu do klasy, więc działają tak, jakbyś napisał je w niej. To sposób na współdzielenie kodu między klasami, które nie mają wspólnego rodzica.

Czy klasa w PHP może używać kilku traitów?

Tak: use Timestamps, SoftDeletes; albo kilka linii use. Jeśli dwa traity definiują metodę o tej samej nazwie, musisz wybrać jedną przez insteadof, na przykład use A, B { A::hello insteadof B; }, a drugą możesz zachować pod nową nazwą przez B::hello as helloB;.

Czym różni się trait od interfejsu w PHP?

Interfejs to typ bez kodu: mówi, jakie metody musi mieć klasa, i działa z nim instanceof. Trait to kod bez typu: wkleja treść metod do klasy, ale $obj instanceof SomeTrait zawsze daje fałsz. Często się je łączy: interfejs dla typu, trait z domyślną implementacją.

Czy trait może mieć konstruktor lub metody abstrakcyjne?

Tak, jedno i drugie. Trait może zadeklarować abstract public function name(): string;, aby wymagać tej metody od klasy, która go używa, i może zdefiniować __construct, choć konstruktor w traicie łatwo koliduje z konstruktorem klasy i rzadko jest dobrym pomysłem.

Która metoda wygrywa, jeśli klasa i trait definiują tę samą metodę?

Własna metoda klasy wygrywa z metodą traitu, a metoda traitu wygrywa z metodą odziedziczoną po klasie rodzica. Kolejność to więc: klasa, potem trait, potem rodzic.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ