Interfejs

Z PHPEdia.pl
Skocz do: nawigacji, wyszukiwania

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.

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.

Więcej informacji

Manual PHP.net