Интерфейсы объектов
 
  Интерфейсы объектов разрешают создавать код, который
  указывает, какие методы и свойства требуется реализовать в классе,
  без определения реализации этих методов или свойств.
  Интерфейсы разделяют пространство имён с классами, трейтами
  и перечислениями, поэтому им нельзя давать одинаковые названия.
 
 
  Интерфейсы определяются так же, как классы, но с ключевым словом
  interface вместо слова class
  и с методами, ни один из которых не определяет содержимое тела.
 
 
  Методы интерфейса объявляются общедоступными,
  что вытекает из самой природы интерфейса.
 
 
  Интерфейсы преследуют две взаимодополняющие цели:
 
 
  - 
   Разрешают разработчикам создавать объекты разноимённых классов, которые умеют взаимно заменять друг друга,
   поскольку реализуют один и тот же интерфейс или интерфейсы.
   Интерфейсы часто внедряют в код, когда требуется создать набор служб доступа к базе данных,
   платёжных шлюзов или стратегий кеширования. Один класс подменяют другим без изменения кода, который его использует.
  
- 
   Разрешают параметру функции или метода принимать и обрабатывать объект, который подчиняется контракту интерфейса,
   чтобы не заботиться о том, что ещё умеет делать объект или как его реализовали.
   Интерфейсы часто называют Iterable,Cacheable,Renderableи другими похожими именами, чтобы описать поведение интерфейса.
  В интерфейсах также определяют
  магические методы, чтобы потребовать от классов,
  которые реализуют интерфейс, реализации этих методов.
 
 Замечание: 
  
   Лучше не включать конструкторы в интерфейсы,
   чтобы не снижать гибкость объекта, который реализует интерфейс,
   хотя включение конструкторов и поддерживается.
   Конструкторы, кроме того, не соблюдают правила наследования, из-за чего поведение иногда становится противоречивым
   и неожиданным.
  
 
 
  Оператор implements
  
   Для реализации интерфейса используется оператор implements.
   Класс должен реализовать все методы, описанные в интерфейсе, иначе
   произойдёт фатальная ошибка. При желании классы могут реализовывать
   более одного интерфейса, разделяя каждый интерфейс запятой.
  
  Внимание
   
    Параметрам в методах класса, в котором реализуется интерфейс, разрешается указывать названия,
    которые не совпадают с названиями параметров в методах интерфейса. Но начиная с PHP 8.0 язык поддерживает
    именованные аргументы, и код, в котором вызываются методы интерфейса,
    часто полагается на названия параметров в интерфейсе.
    Поэтому разработчикам рекомендуют указывать в методах те же названия параметров,
    что и в реализуемом интерфейсе.
   
   
  Замечание: 
   
    Аналогично классам, интерфейсы расширяют оператором
    extends.
   
  
  Замечание: 
   
    Класс, которым реализуется интерфейс, обязан объявить каждый метод интерфейса
    по правилам совместимости сигнатур.
    Реализация методов обязана следовать правилам совместимости сигнатур
    для каждого интерфейса, когда класс реализует больше одного интерфейса,
    в котором объявили методы с одинаковым названием. Поэтому при организации иерархии типов PHP-разработчики
    пользуются доступной в языке ковариантностью и контравариантностью.
   
  
  
 
 
  Константы
  
   Интерфейсы поддерживают объявления констант. Константы интерфейсов
   работают так же, как константы классов.
   До PHP 8.1.0 константы интерфейса нельзя было переопределять в производном классе или интерфейсе.
  
  
  
   Properties
   
    Начиная с PHP 8.4.0 в интерфейсах разрешили объявлять свойства.
    При объявлении свойств потребуется указать, доступно ли свойство для чтения,
    записи или и того, и другого.
    Объявление интерфейса применяется только к открытому доступу на чтение и запись.
   
   
    Класс удовлетворяет свойству интерфейса несколькими способами:
    класс определяет открытое свойство,
    виртуальное свойство,
    которое реализует только тот хук, который соответствует хуку интерфейса,
    или определяет readonly-свойство, которое удовлетворяет свойству интерфейса для чтения.
    Однако в классе нельзя ограничивать доступ на запись свойства модификатором readonly,
    если в интерфейсе свойство объявили доступным для записи.
   
   
    Пример #1 Пример свойств интерфейса
    
<?php
interface I
{
    // Класс, в котором реализуется свойство, ДОЛЖЕН объявить открытое для чтения свойство,
    // но объявление свойства в интерфейсе не ограничивает объявление доступа на запись свойства в классе
    public string $readable {
        get;
    }
    // Класс, в котором реализуется свойство, должен объявить открытое для записи свойство,
    // но объявление свойства в интерфейсе не ограничивает объявление доступа на чтение свойства в классе
    public string $writeable {
        set;
    }
    // Класс, в котором реализуется свойство, должен объявить свойство,
    // открытое как для чтения, так и для записи
    public string $both {
        get;
        set;
    }
}
// Класс реализует каждое из трёх свойств традиционно, без хуков.
// Такая реализация свойств допустима
class C1 implements I
{
    public string $readable;
    public string $writeable;
    public string $both;
}
// Класс реализует каждое из трёх свойств и определяет только те хуки,
// которые потребовал интерфейс. Такая реализация свойств тоже допустима
class C2 implements I
{
    private string $written = '';
    private string $all = '';
    // Класс реализует только хук для чтения, чтобы создать виртуальное свойство.
    // Такое определение удовлетворяет требованию «публичной открытости для чтения».
    // Свойство недоступно для записи, но интерфейс и не требует открытого доступа для записи
    public string $readable {
        get => strtoupper($this->writeable);
    }
    // Интерфейс требует только того, чтобы класс определил свойство, открытое для записи,
    // но включение хука для операции чтения тоже допустимо.
    // Пример создаёт виртуальное свойство, и это нормально
    public string $writeable {
        get => $this->written;
        set {
            $this->written = $value;
        }
    }
    // Свойство требует как операции чтения, так и операции записи,
    // поэтому потребуется либо реализовать оба хука, либо разрешить операциям чтения и записи
    // поведение по умолчанию
    public string $both {
        get => $this->all;
        set {
            $this->all = strtoupper($value);
        }
    }
}
?>
     
    
   
 
  Примеры
  
   Пример #2 Пример интерфейса
   
<?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;
    }
}
// Это не сработает
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (Template::getHtml)
// (Фатальная ошибка: Класс BadTemplate содержит 1 абстрактный метод
// и поэтому требуется объявить класс абстрактным (Template::getHtml))
class BadTemplate implements Template
{
    private $vars = [];
    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }
}
?>
    
   
  
   Пример #3 Наследование интерфейсов
   
<?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) {}
}
?>
    
   
  
   Пример #4 Совместимость с несколькими интерфейсами
   
<?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();
    }
}
?>
    
   
  
   Пример #5 Множественное наследование интерфейсов
   
<?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() {}
}
?>
    
   
  
   Пример #6 Интерфейсы с константами
   
<?php
interface A
{
    const B = 'Константа интерфейса';
}
// Выведет: Константа интерфейса
echo A::B;
class B implements A
{
    const B = 'Константа класса';
}
// Выведет: Константа класса
// До PHP 8.1.0 этот код не сработает,
// поскольку запрещалось переопределять константы
echo B::B;
?>
    
   
  
    Пример #7 Интерфейсы с абстрактными классами
     
<?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;
    }
}
?>
      
    
   
    Пример #8 Одновременное расширение класса и реализация интерфейсов
     
<?php
class One
{
    /* ... */
}
interface Usable
{
    /* ... */
}
interface Updatable
{
    /* ... */
}
// Порядок ключевых слов здесь важен. Слово extends должно идти первым
class Two extends One implements
    Usable,
    Updatable
{
    /* ... */
}
?>
      
    
  
   Интерфейс вместе с объявлениями типов предоставляет надёжный
   способ проверки того, что конкретный объект содержит конкретные
   методы. Смотрите также описание оператора
   instanceof
   и раздел «Объявления типов».