Interfejs w PHP deklaruje metody, które klasa obiecuje zapewnić, bez żadnego kodu: interface Shape { public function area(): float; }. Klasa, która pisze implements Shape, musi zdefiniować area(), a każda funkcja z typem Shape przyjmie tę klasę.
describe() nie wie i nie obchodzi go, którą klasę dostaje. Polega tylko na obietnicy, którą składa interfejs, więc nowa klasa Triangle implements Shape zadziała z nią bez zmiany choćby jednej linii.
Programowanie pod interfejs
Zwykły powód napisania interfejsu to pozwolić kodowi zależeć od zdolności, a nie od konkretnej klasy. Tutaj usługa zamówień przyjmuje dowolny Notifier, więc ten sam kod może wysyłać e-maile na produkcji i zbierać wiadomości w teście:
Implementowanie kilku interfejsów
Klasa rozszerza najwyżej jednego rodzica, ale może implementować tyle interfejsów, ile potrzebuje. Wymień je po implements, oddzielając przecinkami, i zdefiniuj metody ich wszystkich:
JsonSerializable to jeden z wbudowanych interfejsów PHP: jego implementacja decyduje o tym, co json_encode() zwraca dla obiektu.
Stałe interfejsów
Interfejs może deklarować stałe. Każda implementująca klasa je dziedziczy i można je odczytać przez nazwę interfejsu albo nazwę klasy:
W PHP 8.3 interfejsy nie mogą mieć właściwości, tylko metody i stałe. Metody mogą być statyczne: public static function fromArray(array $data): static; w interfejsie zobowiązuje każdą klasę do zdefiniowania tej metody statycznej. Dla stałego zestawu powiązanych wartości enum często pasuje lepiej niż lista stałych w interfejsie.
Interfejs może rozszerzać inne interfejsy
Interfejsy też używają extends i w przeciwieństwie do klas mogą rozszerzać kilka naraz. Klasa implementująca interfejs potomny musi zdefiniować metody całego łańcucha:
Interfejsy wbudowane: Countable, IteratorAggregate, Stringable
PHP ma interfejsy, które podłączają obiekty do języka. Countable sprawia, że działa count($obj), IteratorAggregate sprawia, że działa foreach, a Stringable jest implementowany automatycznie przez każdą klasę z metodą __toString():
Zapomniana metoda
Jeśli klasa deklaruje, że implementuje interfejs, ale brakuje jej metody, PHP zatrzymuje się z błędem krytycznym, który podaje nazwę brakującej metody:
Dodaj metodę total(), a skrypt się wykona. Klasa abstrakcyjna może zostawić metody interfejsu niezdefiniowane i to jest wyjście, które sugeruje komunikat błędu.
Interfejs vs klasa abstrakcyjna
Oba definiują metody, które klasa musi mieć, i oba mogą służyć jako typ. Różnią się tym, co jeszcze mogą zawierać, i tym, ile ich może użyć klasa:
| Interfejs | Klasa abstrakcyjna | |
|---|---|---|
| Treść metod | brak (tylko sygnatury) | metody abstrakcyjne i gotowe |
| Właściwości | nie | tak |
| Konstruktor | może go zadeklarować, rzadko przydatne | tak, z kodem |
| Widoczność metod | tylko public | public, protected, private |
| Stałe | tak | tak |
| Ile na klasę | dowolnie wiele (implements A, B, C) | jedna (extends Base) |
| Typowe użycie | zdolność: Exportable, Countable | wspólny kod dla rodziny: Model, Controller |
Dobrze się łączą: zdefiniuj interfejs do typowania i zaoferuj abstrakcyjną klasę bazową, która go implementuje ze wspólnym kodem. Klasy, które potrzebują innej bazy, nadal mogą implementować interfejs bezpośrednio.
Najczęściej zadawane pytania
Czym jest interfejs w PHP?
Listą sygnatur publicznych metod bez treści: interface Logger { public function log(string $msg): void; }. Klasa, która deklaruje implements Logger, musi zdefiniować każdą z tych metod, a każdy kod z typem Logger przyjmie tę klasę.
Czy klasa w PHP może implementować kilka interfejsów?
Tak. Wymień je po implements, oddzielając przecinkami: class File implements Readable, Writable, Countable. Klasa musi zdefiniować metody ich wszystkich. W ten sposób PHP pokrywa to, co w innych językach daje wielodziedziczenie.
Czym różni się interfejs od klasy abstrakcyjnej w PHP?
Interfejs deklaruje tylko metody publiczne (i stałe); nie ma właściwości ani kodu metod, a klasa może implementować wiele interfejsów. Klasa abstrakcyjna może zawierać właściwości, konstruktory i gotowe metody obok abstrakcyjnych, ale klasa może rozszerzać tylko jedną. Używaj interfejsu dla zdolności („można eksportować”), a klasy abstrakcyjnej dla wspólnego kodu w rodzinie klas.
Czy interfejs w PHP może mieć właściwości?
Nie w PHP 8.3: interfejs może zawierać sygnatury metod i stałe (const VERSION = 2;), ale nie właściwości. Deklaracje właściwości w interfejsach (property hooks) pojawiły się dopiero w PHP 8.4.
Jak sprawdzić, czy obiekt implementuje interfejs w PHP?
Użyj instanceof: $obj instanceof Countable jest prawdą, gdy klasa obiektu implementuje interfejs, bezpośrednio lub przez klasę rodzica. class_implements($obj) zwraca nazwy wszystkich interfejsów implementowanych przez klasę.