Interfejs
Z PHPEdia.pl
Interfejs jest czymś na kształt wzorca dla klas: wymusza żeby klasy, które go implementują zawierały zdefiniowane w nim metody. Interfejs definiuje jedynie, jakie metody "dostępne z zewnątrz" (publiczne) klasa musi zawierać - bez podania ich wewnętrznej budowy i metod prywatnych.
Spis treści |
Dostępność
Interfejsy zostały wprowadzone od PHP 5.0.
Definiowanie interfejsu
Interfejs definiujemy podobnie, jak zwykłą klasę, ale z użyciem słowa kluczowego interface, zamiast class. Definicję wymaganych metod kończymy średnikiem (;), bez podawania żadnej zawartości. Wszystkie metody interfejsu muszą być publiczne, wynika to z natury interfejsu - ma definiować jedynie coś na kształt dostępnego z zewnątrz API klas.
<?php /** * Definicja interfejsu */ interface Testowy { /** * Metoda, którą musi zawierać klasa opierająca się na tym interfejsie */ public function wymaganaMetoda($argument, $innyArgument); /** * Inna wymagana metoda */ public function innaWymaganaMetoda($argument); /* * ... * Definicje innych wymaganych metod * ... */ } ?>
Klasa implementująca interfejs
Klasę, która ma opierać się na interfejsie, definiujemy podobnie do normalnej - z tym, że po nazwie klasy musimy zastosować słówko implements i nazwę interfejsu.
<?php /** * Klasa, która implementuje interfejs Testowy */ class Klasa implements Testowy { /* * Zwykła definicja klasy, w której muszą się znależą metody zdefiniowane w interfejsie Testowy */ /** * Metoda wymagana przez interfejs */ public function wymaganaMetoda($argument, $innyArgument) { /* * ... * Zawartość metody * ... */ } /** * Metoda wymagana przez interfejs */ public function wymaganaMetoda($argument, $innyArgument) { /* * ... * Zawartość metody * ... */ } /* * ... * Inne metody, które nie są wymagane - publiczne lub prywatne * ... */ } ?>
Klasa może implementować kilka interfejsów - wtedy musi zawierać metody zdefiniowane w każdym z nich. W takim przypadku interfejsy wymieniamy po przecinku:
<?php /** * Klasa, która implementuje interfejsy Testowy i InnyInterfejs */ class Klasa implements Testowy, InnyInterfejs { /* * ... * Metody zdefiniowane w interfejsach Testowy i InnyInterfejs * ... */ } ?>
Gdy klasa nie będzie zawierała wszystkich zdefiniowanych w interfejsach metod - będzie to skutkowało błędem krytycznym, a więc zatrzymaniem wykonywania skryptu.
Kiedy warto je stosować?
Zawsze wtedy, gdy potrzebujemy uniwersalnego wzorca dla kilku klas, lub podejrzewamy, że w przyszłości będziemy chcieli wymienić daną klasę na inną (np. korzystając z innego źródła danych), bez potrzeby modyfikowania reszty aplikacji.
Wbudowane interfejsy
PHP, począwszy od wersji 5.0, ma standardowo wbudowane pewne interfejsy - np. te znajdujące się w Standardowej Bibliotece PHP. Są to głównie iteratory.
Przykład zastosowania
Parser adresów URL
Czasami chcemy zastosować w aplikacji URLe w innej niż standardowa (plik.php?argument1=10&argument2=test)postaci, np: plik.php/argument1.10/argument2.test. Wystarczy napisać zwykśy parser, w postaci prostej klasy. Co jednak zrobić, gdy będziemy chcieli kiedyś zmienić postać URLa, np. wrócić do tej standardowej?
Najlepszym rozwiązaniem jest napisanie wtedy nowej klasy do parsowania URLa, jednak z takimi samymi dostępnymi metodami publicznymi - abyśmy nie musieli przebudowywać całej aplikacji.
Tutaj z pomocą przychodzć właśnie interfejsy - już na etapie planowania aplikacji ustalamy, jakie funkcje publiczne (API) będą nam potrzebne, jakie argumenty powinny one przyjmować oraz jakie wartości zwracać. Definiujemy to wszystko tworząc interfejs, np. taki:
<?php /* * Kod zadziałał jedynie w PHP od wersji 5.0 * * Komentarze w formacie phpDoc */ /** * Interfejs dla parsera URL */ interface Url { /** * Konstruktor * * @param string $url URL do sparsowania * @return void */ public function __construct($url); /** * Zwraca parametr wyciągnięty z URLa * * @param string $name Nazwa parametru * @return string Wartość parametru */ public function getParam($name); /** * Zwraca ścieżkę wyciągniętą z URLa * * @return string Scieżka */ public function getPath(); /** * Zwraca nazwę hosta wyciągniętą z URLa * * @return string Nazwa hosta */ public function getHost(); /** * Zwraca prefiks protokołu wyciągnięty z adresu URL * * @return string Prefiks protokołu */ public function getProtocol(); } ?>
Teraz wystarczy, że napiszemy klasą parsującć URL, która implementuje interfejs Url:
<?php /* * Kod zadziałał jedynie w PHP od wersji 5.0 * * Komentarze w formacie phpDoc */ /** * Parser URLa w standardowym formacie */ class StandardUrl implements Url { /** * @var string Niesparsowany URL */ private $url; /** * @var array Parametry wyciągnićte z URLa */ private $params; /** * @var string Nazwa hosta wyciągnićta z URLa */ private $hostName; /** * @var string Ścieżka wyciągnięta z URLa */ private $path; /** * Konstruktor * * @param string $url URL do sparsowania * @return void */ public function __construct($url) { $this->url = $url; try { $this->parse(); } catch (Exception $e) { // ... obsługa błędów ... } } /** * Parsuje URL * * @return void */ private function parse() { /* * ... * Parsowanie URLa: * Wyciąganie z niego parametrów, nazwy hosta, ścieżki, prefiksu protokołu, itp * oraz wwalenie ich do odpowiednich pól klasy * ... */ } /** * Zwraca parametr wyciągnićty z URLa * * @param string $name Nazwa parametru * @return string Wartość parametru */ public function getParam($name) { if (isset ($this->params[$name])) { return $this->params[$name]; } else { throw new Exception('Parametr nie istnieje'); } } /** * Zwraca ścieżkć wyciągniętą z URLa * * @return string Ä?Âącieżka */ public function getPath() { if ($this->path) { return $this->path; } else { throw new Exception('Sciezka nie zostala zdefiniowana'); } } /** * Zwraca nazwę hosta wyciągniętą z URLa * * @return string Nazwa hosta */ public function getHostName() { if ($this->hostName) { return $this->hostName; } else { throw new Exception('Nazwa hosta nie zostala zdefiniowana'); } } } ?>
oprócz metod wymaganych przez interfejs Url, klasa zawiera również metodą parse(), która jest wykorzystywana do parsowania URLa.
Jeśli chcemy zmienić format URLa - wystarczy, że napiszemy nową klasą opartą na interfejsie Url. Struktura wewnątrzna i proces parsowania może być całkiem inny - ważne, żeby klasa zawierała metody zdefiniowane w interfejsie. dzięki temu mamy gwarancję, że taka zmiana nie wpłynie na resztą aplikacji, a ewentualne błędy w implementacji interfejsu Url zostaną wykryte przy uruchomieniu aplikacji.
