Интерфейсы объектов позволяют создавать код, который указывает, какие методы должен реализовать класс, без необходимости определять, как именно они должны быть реализованы. Интерфейсы разделяют пространство имён с классами и трейтами, поэтому они не могут называться одинаково.

Интерфейсы объявляются так же, как и обычные классы, но с использованием ключевого слова interface вместо class. Тела методов интерфейсов должны быть пустыми.

Все методы, определённые в интерфейсах, должны быть общедоступными, что следует из самой природы интерфейса.

На практике интерфейсы используются в двух взаимодополняющих случаях:

  1. Чтобы позволить разработчикам создавать объекты разных классов, которые могут использоваться взаимозаменяемо, поскольку они реализуют один и тот же интерфейс или интерфейсы. Типичный пример — несколько служб доступа к базе данных, несколько платёжных шлюзов или разных стратегий кеширования. Различные реализации могут быть заменены без каких-либо изменений в коде, который их использует.

  2. Чтобы разрешить функции или методу принимать и оперировать параметром, который соответствует интерфейсу, не заботясь о том, что ещё может делать объект или как он реализован. Эти интерфейсы часто называют Iterable, Cacheable, Renderable и так далее, чтобы описать их поведение.

Интерфейсы могут определять магические методы, требуя от реализующих классов реализации этих методов.

(звезда) Замечание:

Хотя они поддерживаются, использование конструкторов в интерфейсах настоятельно не рекомендуется. Это значительно снижает гибкость объекта, реализующего интерфейс. Кроме того, к конструкторам не применяются правила наследования, что может привести к противоречивому и неожиданному поведению.

Ключевое слово implements

Для реализации интерфейса используется оператор implements. Класс должен реализовать все методы, описанные в интерфейсе, иначе произойдёт фатальная ошибка. При желании классы могут реализовывать более одного интерфейса, разделяя каждый интерфейс запятой.

(warning) Класс может реализовать два интерфейса, которые определяют метод с тем же именем, только если объявление метода в обоих интерфейсах идентично.

(warning) Класс, реализующий интерфейс, может использовать для своих параметров имя, отличное от имени интерфейса. Однако, начиная с PHP 8.0, в языке поддерживаются именованные аргументы, и вызывающий код может полагаться на имя параметра в интерфейсе. По этой причине настоятельно рекомендуется, чтобы разработчики использовали те же имена параметров, что и реализуемый интерфейс.

(звезда) Интерфейсы могут быть унаследованы друг от друга, так же, как и классы, с помощью оператора extends.

(звезда) Класс, реализующий интерфейс, должен объявить все методы в интерфейсе с совместимой сигнатурой.

Константы (Constants)

Интерфейсы могут содержать константы. Константы интерфейсов работают точно так же, как и константы классов, за исключением того, что они не могут быть переопределены наследующим классом или интерфейсом.

Примеры

Пример #1 Пример интерфейса

<?php

// Объявим интерфейс 'Template'
interface Template
{
    public function setVariable($name, $var);
    public function getHtml($template);
}

// Реализация интерфейса
// Это будет работать
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;
    }
}

// Это не будет работать
// (Фатальная ошибка: Класс BadTemplate содержит 1 абстрактный метод
// и поэтому должен быть объявлен абстрактным (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);
}

// Это сработает
class C implements B
{
    public function foo()
    {
    }

    public function baz(Baz $baz)
    {
    }
}

// Это не сработает и выдаст фатальную ошибку
class D implements B
{
    public function foo()
    {
    }

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

Пример #3 Множественное наследование интерфейсов

<?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()
    {
    }
}
?>

Пример #4 Интерфейсы с константами

<?php

interface A
{
    const B = 'Константа интерфейса';
}

// Выведет: Константа интерфейса
echo A::B;

// Это, однако, не будет работать, так как
// константы переопределять нельзя.
class B implements A
{
    const B = 'Константа класса';
}
?>

Пример #5 Интерфейсы с абстрактными классами

<?php

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

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

// Абстрактный класс может реализовывать только часть интерфейса.
// Классы, расширяющие абстрактный класс, должны реализовать все остальные.
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;
    }
}

$c = new C();
echo $c->bar(2);        // OK, 4

$b = new B();
$b->foo("123");         // error: Cannot instantiate abstract class B

?>

https://3v4l.org/nSJIF

Пример #6 Одновременное расширение и внедрение

<?php

class One
{
    /* ... */
}

interface Usable
{
    /* ... */
}

interface Updatable
{
    /* ... */
}

// Порядок ключевых слов здесь важен. "extends" должно быть первым.
class Two extends One implements Usable, Updatable
{
    /* ... */
}
?>

Интерфейс, совместно с объявлениями типов, предоставляет отличный способ проверки того, что определённый объект содержит определённый набор методов. Смотрите также оператор instanceof и объявление типов.


Источники:

(1) https://www.php.net/manual/ru/language.oop5.interfaces.php

Tags

Нет комментариев

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.