6. Інтерфейси
Інтерфейс — це структура, що визначає прототипи методів. Клас, який реалізує інтерфейс, повинен визначити код усіх методів цього інтерфейсу.
6.1. Дерево скриптів

6.2. Перший інтерфейс
Ми визначаємо інтерфейс [IExemple] у скрипті [IExemple.php]. У цьому ж скрипті ми визначаємо два класи, що реалізують інтерфейс [IExemple]:
<?php
// суворе дотримання типів параметрів функцій
declare(strict_types=1);
// простір імен
namespace Exemples;
// інтерфейс
interface IExemple {
// методи інтерфейсу
public function ajouter(int $i, int $j): int;
public function soustraire(int $i, int $j): int;
}
// реалізація 1 інтерфейсу 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;
}
}
// реалізація 2 інтерфейсу 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;
}
}
Коментарі
- рядки 10–14: інтерфейс IExemple визначає два методи: функцію ajouter (рядок 12) та функцію soustraire (рядок 13). Інтерфейс не визначає код цих методів. Це роблять класи, що реалізують інтерфейс;
- рядок 17: клас Classe1 реалізує (implements) інтерфейс IExemple. Отже, він визначає код для методів ajouter (рядок 18) та soustraire (рядок 22);
- рядки 29–38: те саме стосується класу Classe2;
Ми використовуємо інтерфейс [IExemple] у наступному скрипті [interfaces-01.php]:
<?php
require_once "IExemple.php";
use \Exemples\Classe1;
use \Exemples\Classe2;
use \Exemples\IExemple;
// функція
function calculer(int $a, int $b, IExemple $interface) : void {
print $interface->ajouter($a, $b)."\n";
print $interface->soustraire($a, $b)."\n";
}
// ------------------- main
// створення 2 об’єктів типів Classe1 та Classe2
$c1 = new Classe1();
$c2 = new Classe2();
// виклик функції «calculer»
calculer(4, 3, $c1);
calculer(14, 13, $c2);
Коментарі
- рядки 3–6: включення та визначення класів та інтерфейсів, що використовуються скриптом;
- рядок 3: замість оператора include тут використано оператор require_once. Інструкція include включає файл у поточний скрипт, навіть якщо він уже був включений, тоді як інструкція require_once включає його лише один раз. Тому щодо включення коду краще використовувати інструкцію require_once;
- рядки 9–12: визначаємо функцію;
- рядок 9: тут ми оголосили типи трьох параметрів. Якщо під час виконання фактичний параметр не відповідає очікуваному типу, генерується помилка. Третій параметр має тип [IExemple]. Це означає, що фактичний параметр має бути екземпляром класу, що реалізує інтерфейс [IExemple], отже, у нашому прикладі — екземпляром класів [Classe1] або [Classe2];
- рядки 10–11: ми використовуємо методи [ajouter, soustraire] інтерфейсу [IExemple];
- рядок 19: третій фактичний параметр має тип [Classe1], сумісний із типом [IExemple] третього формального параметра функції;
- рядок 20: те саме стосується класу [Classe2];
Результати
6.3. Похідний інтерфейс
Так само, як дочірній клас може успадковувати батьківський клас, дочірній інтерфейс може успадковувати батьківський інтерфейс.
Розглянемо інтерфейс [IExemple2], визначений у файлі [IExemple2.php]:
<?php
// суворе дотримання типів параметрів функцій
declare(strict_types=1);
// простір імен
namespace Exemples;
// включення визначення інтерфейсу IExemple
require_once __DIR__."/IExemple.php";
// інтерфейс, що походить від IExemple
interface IExemple2 extends IExemple {
// метод інтерфейсу
public function multiplier(int $i, int $j): int;
}
// реалізація інтерфейсу IExemple2
class Classe3 extends Classe2 implements IExemple2 {
public function multiplier(int $a, int $b): int {
return $a * $b;
}
}
Коментарі
- рядок 6: інтерфейс [IExemple2] буде в тому самому просторі імен, що й інтерфейс [Iexemple];
- рядок 9: включено файл визначення інтерфейсу [Iexemple];
- рядки 12–15: визначають інтерфейс [IExemple2], який розширює (успадковує) інтерфейс [Iexemple] (extends) та додає до нього новий метод (рядок 14);
- рядки 18–24: клас [Classe3] розширює клас [Classe2]. Оскільки останній реалізує інтерфейс [IExemple], це також стосуватиметься класу [Classe3]. Дійсно, [Classe3] успадковує всі публічні та захищені методи класу [Classe2], а отже, і методи додавання та віднімання. Крім того, зазначено, що [Classe3] реалізує інтерфейс [IExemple2]. Відповідно, він також повинен реалізовувати метод «multiplier», чого не робить [Classe2]. Саме це роблять рядки 20–22;
Інтерфейс [IExemple2] використовується наступним скриптом [interfaces-02]:
<?php
// суворе дотримання типів параметрів функцій
declare(strict_types=1);
// імпорт та кваліфікація класів та інтерфейсів, необхідних для скрипта
require_once __DIR__."/Iexemple2.php";
use \Exemples\IExemple2;
use \Exemples\Classe3;
// функція
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
// створення 1 об’єкта типу Classe3, який реалізує IExemple2
$c3 = new Classe3();
// виклик функції «calculer»
calculer(4, 3, $c3);
Коментарі
- рядок 12: третій параметр функції [calculer] має тип [IExemple2];
- рядки 13–15: використовуються три методи інтерфейсу [IExemple2];
- рядок 22: викликається функція [calculer], де третім параметром є об’єкт типу [IExemple2] (рядок 20);
Результати
6.4. Передача інтерфейсу як параметра функції
У попередньому розділі ми бачили, що коли очікуваний тип для параметра функції є класом, то фактичним параметром може бути об’єкт очікуваного типу або його похідного. Те саме стосується інтерфейсів, як показано в наступному скрипті [interfaces-03.php]:
<?php
// суворе дотримання типів параметрів функцій
declare(strict_types=1);
// імпорт та кваліфікація класів та інтерфейсів, необхідних для скрипта
require_once __DIR__."/IExemple.php";
use \Exemples\IExemple;
require_once __DIR__."/IExemple2.php";
use \Exemples\Classe3;
// функція
function calculer(int $a, int $b, IExemple $interface) {
print $interface->ajouter($a, $b) . "\n";
print $interface->soustraire($a, $b) . "\n";
}
// ------------------- main
// створення 1 об’єкта типу Classe3, який реалізує IExemple2, а отже, і IExemple
$c3 = new Classe3();
// виклик функції «calculer»
calculer(4, 3, $c3);
Коментарі
- у рядку 13 функція [calculer] очікує як третій параметр тип [IExemple]. Це означає, що тип фактичного параметра може бути типом [IExemple] або похідним від нього;
- рядок 20: створюється екземпляр об’єкта $c3 типу [Classe3], клас якого реалізує інтерфейс [IExemple2], який, у свою чергу, розширює інтерфейс [IExemple]. Отже, зрештою $c3 реалізує інтерфейс [IExemple];
- рядок 22: викликається функція [calculer], третім параметром якої є об’єкт $c3 типу [Classe3]. Завдяки взаємодії успадкування класів та реалізацій ми побачили, що об’єкт $c3 реалізує тип [IExemple]. Отже, його можна використовувати як третій параметр;
Результати