6. Interfejsy
Interfejs to struktura definiująca prototypy metod. Klasa implementująca interfejs musi zdefiniować kod wszystkich metod tego interfejsu.
6.1. Drzewo skryptów

6.2. Pierwszy interfejs
Definiujemy interfejs [IExemple] w skrypcie [IExemple.php]. W tym samym skrypcie definiujemy dwie klasy implementujące interfejs [IExemple]:
<?php
// ścisłe przestrzeganie typów parametrów funkcji
declare(strict_types=1);
// przestrzeń nazw
namespace Exemples;
// interfejs
interface IExemple {
// metody interfejsu
public function ajouter(int $i, int $j): int;
public function soustraire(int $i, int $j): int;
}
// implementacja 1 interfejsu IExemple
class Classe1 implements IExemple {
public function ajouter(int $a, int $b): int {
return $a + $b + 10;
}
public function soustraire(int $a, int $b): int {
return $a - $b + 20;
}
}
// implementacja 2 interfejsu IExemple
class Classe2 implements IExemple {
public function ajouter(int $a, int $b) : int{
return $a + $b + 100;
}
public function soustraire(int $a, int $b) : int {
return $a - $b + 200;
}
}
Komentarze
- wiersze 10–14: interfejs IExemple definiuje dwie metody: funkcję ajouter (wiersz 12) oraz funkcję soustraire (wiersz 13). Interfejs nie definiuje kodu tych metod. Zrobią to klasy implementujące ten interfejs;
- wiersz 17: klasa Classe1 implementuje interfejs IExemple. W związku z tym definiuje ona kod dla metod ajouter (wiersz 18) i soustraire (wiersz 22);
- wiersze 29–38: to samo dotyczy klasy Classe2;
Korzystamy z interfejsu [IExemple] w poniższym skrypcie [interfaces-01.php]:
<?php
require_once "IExemple.php";
use \Exemples\Classe1;
use \Exemples\Classe2;
use \Exemples\IExemple;
// funkcja
function calculer(int $a, int $b, IExemple $interface) : void {
print $interface->ajouter($a, $b)."\n";
print $interface->soustraire($a, $b)."\n";
}
// ------------------- main
// utworzenie 2 obiektów typu Klasa1 i Klasa2
$c1 = new Classe1();
$c2 = new Classe2();
// wywołanie funkcji „oblicz”
calculer(4, 3, $c1);
calculer(14, 13, $c2);
Komentarze
- wiersze 3–6: dołączenie i definicja klas oraz interfejsów wykorzystywanych przez skrypt;
- wiersz 3: zamiast instrukcji include użyliśmy tutaj instrukcji require_once. Instrukcja include dołącza plik do bieżącego skryptu, nawet jeśli został on już wcześniej dołączony, podczas gdy instrukcja require_once dołącza go tylko raz. Dlatego w przypadku dołączania kodu preferowana jest instrukcja require_once;
- wiersze 9–12: definiujemy funkcję;
- wiersz 9: zadeklarowaliśmy tutaj typy trzech parametrów. Jeśli podczas wykonywania rzeczywisty parametr nie jest typu oczekiwanego, zgłaszany jest błąd. Trzeci parametr jest typu [IExemple]. Oznacza to, że rzeczywisty parametr musi być instancją klasy implementującej interfejs [IExemple], a więc w naszym przykładzie instancją klasy [Classe1] lub [Classe2];
- wiersze 10–11: wykorzystujemy metody [ajouter, soustraire] z interfejsu [IExemple];
- wiersz 19: trzeci parametr rzeczywisty jest typu [Classe1], zgodnego z typem [IExemple] trzeciego parametru formalnego funkcji;
- wiersz 20: to samo dotyczy klasy [Classe2];
Wyniki
6.3. Wyprowadzanie interfejsu
Podobnie jak klasa potomna może rozszerzać klasę nadrzędną, tak samo interfejs potomny może rozszerzać interfejs nadrzędny.
Rozważmy interfejs [IExemple2] zdefiniowany w pliku [IExemple2.php]:
<?php
// ścisłe przestrzeganie typów parametrów funkcji
declare(strict_types=1);
// przestrzeń nazw
namespace Exemples;
// włączenie definicji interfejsu IExemple
require_once __DIR__."/IExemple.php";
// interfejs wywodzący się z IExemple
interface IExemple2 extends IExemple {
// metoda interfejsu
public function multiplier(int $i, int $j): int;
}
// implementacja interfejsu IExemple2
class Classe3 extends Classe2 implements IExemple2 {
public function multiplier(int $a, int $b): int {
return $a * $b;
}
}
Uwagi
- wiersz 6: interfejs [IExemple2] będzie znajdował się w tej samej przestrzeni nazw co interfejs [Iexemple];
- wiersz 9: dołączamy plik definicji interfejsu [Iexemple];
- wiersze 12–15: definiują interfejs [IExemple2], który rozszerza (dziedziczy) interfejs [Iexemple] (extends) i dodaje do niego nową metodę (wiersz 14);
- wiersze 18–24: klasa [Classe3] rozszerza klasę [Classe2]. Ponieważ ta ostatnia implementuje interfejs [IExemple], to samo dotyczy klasy [Classe3]. W rzeczywistości klasa [Classe3] dziedziczy wszystkie metody publiczne i chronione klasy [Classe2], a więc również metody „dodaj” i „odejmij”. Ponadto wskazano, że klasa [Classe3] implementuje interfejs [IExemple2]. W związku z tym musi ona również implementować metodę „multiplier”, czego nie robi klasa [Classe2]. Odpowiadają za to wiersze 20–22;
Interfejs [IExemple2] jest wykorzystywany przez następujący skrypt [interfaces-02]:
<?php
// ścisłe przestrzeganie typów parametrów funkcji
declare(strict_types=1);
// dołączenie i kwalifikatory klas oraz interfejsów niezbędnych dla skryptu
require_once __DIR__."/Iexemple2.php";
use \Exemples\IExemple2;
use \Exemples\Classe3;
// funkcja
function calculer(int $a, int $b, IExemple2 $interface) {
print $interface->ajouter($a, $b) . "\n";
print $interface->soustraire($a, $b) . "\n";
print $interface->multiplier($a, $b) . "\n";
}
// ------------------- main
// utworzenie 1 obiektu typu Classe3, który implementuje IExemple2
$c3 = new Classe3();
// wywołanie funkcji „calculer”
calculer(4, 3, $c3);
Komentarze
- wiersz 12: trzeci parametr funkcji [calculer] jest typu [IExemple2];
- wiersze 13–15: wykorzystuje się trzy metody interfejsu [IExemple2];
- wiersz 22: wywołuje się funkcję [calculer], podając jako trzeci parametr obiekt typu [IExemple2] (wiersz 20);
Wyniki
6.4. Przekazywanie interfejsu jako parametru funkcji
W poprzednim akapicie widzieliśmy, że gdy oczekiwanym typem parametru funkcji jest klasa, rzeczywistym parametrem może być obiekt typu oczekiwanego lub pochodnego. To samo dotyczy interfejsów, co pokazuje poniższy skrypt [interfaces-03.php]:
<?php
// ścisłe przestrzeganie typów parametrów funkcji
declare(strict_types=1);
// dołączenie i kwalifikacja klas oraz interfejsów niezbędnych dla skryptu
require_once __DIR__."/IExemple.php";
use \Exemples\IExemple;
require_once __DIR__."/IExemple2.php";
use \Exemples\Classe3;
// funkcja
function calculer(int $a, int $b, IExemple $interface) {
print $interface->ajouter($a, $b) . "\n";
print $interface->soustraire($a, $b) . "\n";
}
// ------------------- main
// utworzenie 1 obiektu typu Classe3, który implementuje IExemple2, a tym samym IExemple
$c3 = new Classe3();
// wywołanie funkcji „oblicz”
calculer(4, 3, $c3);
Komentarze
- w wierszu 13 funkcja [calculer] oczekuje jako trzeciego parametru typu [IExemple]. Oznacza to, że typ rzeczywistego parametru może być typu [IExemple] lub pochodnego;
- wiersz 20: tworzona jest instancja obiektu $c3 typu [Classe3], klasy, która implementuje interfejs [IExemple2], który z kolei rozszerza interfejs [IExemple]. Ostatecznie więc $c3 implementuje interfejs [IExemple];
- wiersz 22: wywoływana jest funkcja [calculer] z obiektem $c3 typu [Classe3] jako trzecim parametrem. Dzięki dziedziczeniu klas i implementacjom widzieliśmy, że obiekt $c3 implementuje typ [IExemple]. Można go zatem użyć jako trzeciego parametru;
Wyniki