Object Interfaces

Оновлено: 12.05.2023

Інтерфейси об'єктів дозволяють створювати код, який визначає, які методи повинен реалізовувати клас, без необхідності визначати, як ці методи реалізуються. Інтерфейси поділяють простір імен з класами та рисами, тому вони не можуть використовувати однакові імена.

Інтерфейси визначаються так само, як і класи, але ключове слово interface замінює ключове слово class і не визначає вміст жодного з методів.

Усі методи, оголошені в інтерфейсі, мають бути загальнодоступними; це природа інтерфейсу.

На практиці інтерфейси служать двом взаємодоповнюючим цілям:

Дозволити розробникам створювати об'єкти різних класів, які можна використовувати взаємозамінно, оскільки вони реалізують один і той самий інтерфейс або інтерфейси. Поширеним прикладом є декілька служб доступу до баз даних, декілька платіжних шлюзів або різні стратегії кешування. Різні реалізації можуть бути замінені без внесення змін до коду, який їх використовує. Дозволити функції або методу приймати та оперувати з параметром, який відповідає інтерфейсу, не переймаючись тим, що ще може робити об'єкт або як він реалізований. Ці інтерфейси часто називаються Iterable, Cacheable, Renderable тощо, щоб описати важливість поведінки.

Інтерфейси можуть визначати магічні методи, які вимагають від класів, що їх реалізують, реалізовувати ці методи.

Зауважте:

Хоча вони підтримуються, включати конструктори в інтерфейси категорично не рекомендується. Це значно зменшує гнучкість об'єкта, що реалізує інтерфейс. Крім того, конструктори не підпадають під дію правил успадкування, що може спричинити непослідовну та неочікувану поведінку.

Для реалізації інтерфейсу використовується оператор implements. Усі методи інтерфейсу повинні бути реалізовані в класі; якщо цього не зробити, то виникне фатальна помилка. Класи можуть реалізовувати більше одного інтерфейсу за бажанням, відокремлюючи кожен інтерфейс комою.

Клас, що реалізує інтерфейс, може використовувати для своїх параметрів імена, відмінні від імен інтерфейсу. Однак, починаючи з версії PHP 8.0, мова підтримує іменовані аргументи, а це означає, що виклики можуть покладатися на ім'я параметра в інтерфейсі. З цієї причини настійно рекомендується, щоб розробники використовували ті ж імена параметрів, що і в інтерфейсі, який реалізується.

Зауважте:

Інтерфейси можна розширювати як класи за допомогою оператора extends.

Зауважте:

Клас, що реалізує інтерфейс, повинен оголошувати всі методи інтерфейсу з сумісною сигнатурою. Клас може реалізовувати декілька інтерфейсів, які оголошують метод з однаковим іменем. У цьому випадку реалізація повинна відповідати правилам сумісності сигнатур для всіх інтерфейсів. Таким чином, може бути застосовано коваріацію та контраваріантність.

Інтерфейси можуть мати константи. Константи інтерфейсу працюють так само, як і константи класу. До версії PHP 8.1.0 вони не можуть бути перевизначені класом/інтерфейсом, який їх успадковує.

Приклад #1 Приклад інтерфейсу

<?php

// Declare the interface 'Template'
interface Template
{
    public function setVariable($name, $var);
    public function getHtml($template);
}

// Implement the interface
// This will work
class WorkingTemplate implements Template
{
    private $vars = [];
  
    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }
  
    public function getHtml($template)
    {
        foreach($this->vars as $name => $value) {
            $template = str_replace('{' . $name . '}', $value, $template);
        }
 
        return $template;
    }
}

// This will not work
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (Template::getHtml)
class BadTemplate implements Template
{
    private $vars = [];
  
    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }
}
?>

Приклад #2 Розширювані інтерфейси

<?php
interface A
{
    public function foo();
}

interface B extends A
{
    public function baz(Baz $baz);
}

// This will work
class C implements B
{
    public function foo()
    {
    }

    public function baz(Baz $baz)
    {
    }
}

// This will not work and result in a fatal error
class D implements B
{
    public function foo()
    {
    }

    public function baz(Foo $foo)
    {
    }
}
?>

Приклад #3 Сумісність дисперсії з декількома інтерфейсами

<?php
class Foo {}
class Bar extends Foo {}

interface A {
    public function myfunc(Foo $arg): Foo;
}

interface B {
    public function myfunc(Bar $arg): Bar;
}

class MyClass implements A, B
{
    public function myfunc(Foo $arg): Bar
    {
        return new Bar();
    }
}
?>

Приклад #4 Множинне успадкування інтерфейсів

<?php
interface A
{
    public function foo();
}

interface B
{
    public function bar();
}

interface C extends A, B
{
    public function baz();
}

class D implements C
{
    public function foo()
    {
    }

    public function bar()
    {
    }

    public function baz()
    {
    }
}
?>

Приклад #5 Інтерфейси з константами

<?php
interface A
{
    const B = 'Interface constant';
}

// Prints: Interface constant
echo A::B;


class B implements A
{
    const B = 'Class constant';
}

// Prints: Class constant
// Prior to PHP 8.1.0, this will however not work because it was not
// allowed to override constants.
echo B::B;
?>

Приклад #6 Інтерфейси з абстрактними класами

<?php
interface A
{
    public function foo(string $s): string;

    public function bar(int $i): int;
}

// An abstract class may implement only a portion of an interface.
// Classes that extend the abstract class must implement the rest.
abstract class B implements A
{
    public function foo(string $s): string
    {
        return $s . PHP_EOL;
    }
}

class C extends B
{
    public function bar(int $i): int
    {
        return $i * 2;
    }
}
?>

Приклад #7 Одночасне розширення та впровадження

<?php

class One
{
    /* ... */
}

interface Usable
{
    /* ... */
}

interface Updatable
{
    /* ... */
}

// The keyword order here is important. 'extends' must come first.
class Two extends One implements Usable, Updatable
{
    /* ... */
}
?>

Інтерфейс, разом з оголошеннями типів, надає хороший спосіб переконатися, що певний об'єкт містить певні методи. Див. розділ про оператор instanceof та оголошення типів.