Что такое интерфейс в php
Перейти к содержимому

Что такое интерфейс в php

  • автор:

Интерфейсы в PHP

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

Что такое интерфейс?

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

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

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

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

Пример

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

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

Пример

Примечание: Интерфейсы не должны объявлять конструкторы или деструкторы, поскольку это детали реализации на уровне класса.

Как реализовать интерфейс?

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

Пример
 class Child implements interfaceName < // определяет методы интерфейса и может иметь собственный код >?>

В приведенном ниже примере мы создадим интерфейс для классов, управляющих автомобилями, который передает все свои методы setModel() и getModel() классам, реализующим интерфейс:

Пример

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

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

Пример
 class miniCar implements Car < private $model; public function setModel($name) < $this ->model = $name; > public function getModel() < return $this ->model; > > $car = new miniCar(); $car -> setModel('Mercedes Benz'); echo $car -> getModel(); ?>

Результат выполнения кода:

Mercedes Benz

Реализация нескольких интерфейсов

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

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

Пример
 Interface MyInterface2 < public function myMethod2(); >class MyClass implements MyInterface1, MyInterface2 < public function myMethod1() < echo "Привет "; >public function myMethod2() < echo "Мир!"; >> $obj = new MyClass(); $obj -> myMethod1(); $obj -> myMethod2(); ?>

Результат выполнения кода:

Привет Мир!

Как расширить класс и реализовать интерфейс?

Класс может расширять класс, а также реализовывать один или несколько интерфейсов:

Пример
 class ParentClass < public $name; public function __construct($name) < $this ->name = $name; > > class ChildClass extends ParentClass implements MyInterface < function write() < echo $this ->name; > > $child = new ChildClass('Max Maxsimov'); $child -> write(); ?>

Результат выполнения кода:

Max Maxsimov

Примечание: Вы можете расширить только один класс, в то же время вы можете реализовать несколько интерфейсов.

Расширение интерфейсов

Интерфейсы также могут расширять интерфейсы:

Пример
 Interface MyInterface2 extends MyInterface1 < public function myMethod2(); >class MyClass1 implements MyInterface1 < # нужен только этот метод public function myMethod1() <>> class MyClass2 implements MyInterface2 < # должны быть объявлены как myMethod1, так и myMethod2 public function myMethod1() <>public function myMethod2() <> > ?>

Разница между абстрактным классом и интерфейсом

В таблице приведены несколько важных различий между абстрактным классом и интерфейсом:

интерфейс абстрактный класс
код — абстрактные методы
— константы
— абстрактные методы
— константы
— конкретные методы
— конкретные переменные
модификаторы доступа — публичные — публичные
— защищенные
количество родителей Один и тот же класс может реализовывать более 1 интерфейса Дочерний класс может наследовать только от 1 абстрактного класса

Заключение

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

Что такое интерфейс в php

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

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

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

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

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

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

Замечание:

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

implements

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

Внимание

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

Замечание:

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

Замечание:

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

Константы

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

Примеры

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

// Объявим интерфейс ‘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 ( » , $value , $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 ;
>
>
?>

Пример #2 Наследование интерфейсов

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 ()
>

Пример #3 Совместимость с несколькими интерфейсами

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 Множественное наследование интерфейсов

interface B
public function bar ();
>

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

class D implements C
public function foo ()
>

public function bar ()
>

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

interface A
const B = ‘Константа интерфейса’ ;
>

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

class B implements A
const B = ‘Константа класса’ ;
>

// Выведет: Константа класса
// До PHP 8.1.0 этот код не будет работать,
// потому что было нельзя переопределять константы.
echo B :: B ;
?>

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

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 ;
>
>
?>

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

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

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

Интерфейсы — PHP: Введение в ООП

Вместе с классами в PHP широко используется языковая конструкция «интерфейс». В этом уроке мы рассмотрим техническую сторону вопроса, а потом поговорим о смысле. Про последнее я сейчас могу сказать немного, потому что полноценный разговор про суть интерфейсов у нас пойдёт во время изучения полиморфизма в последующих курсах. А пока достаточно иметь общее представление об интерфейсах, так как без них не получится окунуться во фреймворки.

Интерфейс в PHP — конструкция языка, описывающая абстрактный тип данных (АТД). Напомню, что АТД определяет набор операций (функций), независимых от конкретной реализации типа (в нашем случае класса) для манипулирования его значениями. На практике интерфейсы содержат определения функций (то есть описание их сигнатур) без их реализации.

Хотя данная конструкция для нас в новинку, само понятие интерфейса используется на протяжении всего курса. В первую очередь это рассуждения о типах. Для оперирования точками на плоскости нам не нужна «реализация» точек. Достаточно того, что мы представляем их визуально и знаем операции, выполняемые над ними. То же самое касается и более базовых концепций, например, чисел и любых арифметических операций. Задумывались ли вы над тем, как на самом деле выполняются арифметические операции? Ответ на этот вопрос гораздо сложнее, чем может показаться на первый взгляд, и он зависит не только от языка, но и от конкретного аппаратного обеспечения (железа). Однако незнание ответа не мешает нам пользоваться числами, строками и массивами, не зная их устройства.

 // file: DecartPointInterface.php namespace App; // Интерфейсы, по аналогии с классами, хранятся в своих собственных файлах // и загружаются автоматически при следовании стандарту PSR-4. // Имя интерфейса может быть любым, главное — соответствие PSR-4. interface DecartPointInterface  public function __construct($x, $y); public function getX(); public function getY(); > 

То, что раньше мы описывали словами и держали в голове, теперь явно записано в виде кода. Декартова точка — это АТД с тремя операциями:

  • Создание точки из двух значений
  • Извлечение координаты X
  • Извлечение координаты Y

По сути, прикладному коду больше ничего знать о точках и не нужно. Естественно, если нам понадобятся новые операции, то мы всегда можем их добавить, тем самым расширив интерфейс. Свои собственные АТД можно менять как угодно и когда угодно, только учтите, что изменение интерфейса обычно приводит к необходимости править код, использующий его.

Сама по себе конструкция Interface никак не влияет на остальной код. Недостаточно просто создать интерфейс, в этом нет смысла. Интерфейс должен быть реализован, и тогда он начнёт приносить пользу.

 namespace AnotherApp; // Импорт интерфейса use App\DecartPointInterface; class DecartPoint implements DecartPointInterface  private $x; private $y; // Интерфейсные функции public function __construct($x, $y)  $this->x = $x; $this->y = $y; > public function getX()  return $this->x; > public function getY()  return $this->y; > // Не интерфейсные функции public function __toString()  return "($this->getX()>, $this->getY()>)"; > > 

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

Насколько наличие интерфейсов критично для PHP программ? Например, можно в любой программе открыть все файлы с классами и удалить часть определения класса, которая описывает реализацию интерфейсов (слово implements и то, что идёт за ним). После этого не изменится ровным счётом ничего — программа продолжит выполняться так же, как и выполнялась. Но ситуация меняется, если использовать интерфейс в сигнатурах функций и методов вместо классов.

 function compare(DecartPointInterface $point1, DecartPointInterface $point2)  // . > 

Во время выполнения программы PHP проверяет, реализует ли класс соответствующий интерфейс, и если нет, то возникает ошибка. Причём проверка идёт именно на наличие записи implements в определении класса, а не на факт того, что методы определены (проверка реализации интерфейса гарантирует это).

Такая запись позволяет коду завязываться не на конкретную реализацию точек, а на их интерфейс. Это — ключевая мысль, которую имеет смысл обсуждать подробнее вместе с полиморфизмом.

Отдельно стоит сказать, что один класс может реализовывать любое число интерфейсов, в таком случае они описываются через запятую:

 // Пример из библиотеки DS, внутри которой реализованы различные структуры данных в объектном синтаксисе // https://github.com/php-ds/polyfill/blob/master/src/Stack.php class Stack implements \IteratorAggregate, \ArrayAccess, Collection  // some code > 

Здесь класс Stack реализует сразу три интерфейса. Это значит, что внутри него должны быть реализованы методы всех указанных интерфейсов.

Если у разных интерфейсов окажутся методы с одинаковым названием, но разной сигнатурой, то возникнет ошибка. В таком случае придётся отказаться от одного из интерфейсов либо переделать их (например, переименовать методы)

Интерфейс Countable

В PHP встроен интерфейс Countable , а функция count умеет работать с любым объектом, реализующим этот интерфейс.

 class Collection implements Countable  private $items; public function __construct($items = [])  $this->items = $items; > public function count()  return sizeof($this->items); > > $coll = new Collection([3, 2, 5]); print_r(count($coll)); // => 3 

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов

Наши выпускники работают в компаниях:

Интерфейсы в PHP

Сегодня мы поговорим об интерфейсах в PHP. Прежде чем начать разговор непосредственно об интерфейсах, давайте сначала я расскажу вам о том, какую проблему они решают и почему в них появилась необходимость.

Интерфейсы в PHP мы будем изучать на примере геометрических фигур. Пусть у нас есть прямоугольник, квадрат и круг, и, например, мы хотим вычислить их площади. Мы прекрасно помним, что для вычисления площади прямоугольника нам нужно знать длины двух соседних сторон, для квадрата – длину одной стороны, для круга – его радиус. Давайте создадим классы, которые будут описывать свойства этих фигур, а также создадим методы, для вычисления их площади.

class Rectangle < private $x; private $y; public function __construct(float $x, float $y) < $this->x = $x; $this->y = $y; > public function calculateSquare(): float < return $this->x * $this->y; > >
class Square < private $x; public function __construct(float $x) < $this->x = $x; > public function calculateSquare(): float < return $this->x ** 2; > >
class Circle < private $r; public function __construct(float $r) < $this->r = $r; > public function calculateSquare(): float < $pi = 3.1416; return $pi * ($this->r ** 2); > >

Константы класса

Число Пи мы здесь задали в переменную, однако для таких вот постоянных вещей, которые в процессе работы программы не изменяются, лучше использовать константы. Они определяются с помощью слова const. Вот так:

const PI = 3.1416;

Константы принято задавать в самом начале класса и называть их CAPS-ом с подчеркушками. Вот примеры того, как могут называться константы: DB_NAME, COUNT_OF_OBJECTS.

  • Курс HTML для начинающих
  • Курс PHP для начинающих
  • Курс MySQL для начинающих
  • Курс ООП в PHP

Для того, чтобы обратиться к константе, нужно использовать конструкцию self::ИМЯ_КОНСТАНТЫ, или ИмяКласса::ИМЯ_КОНСТАНТЫ. Ключевое слово self – это обращение к текущему классу (как $this – обращение к текущему объекту, не путайте эти понятия). Константы принадлежат классу, а не его объектам.

Давайте вынесем число Пи в константу.

class Circle < const PI = 3.1416; private $r; public function __construct(float $r) < $this->r = $r; > public function calculateSquare(): float < return self::PI * ($this->r ** 2); > >

Теперь мы можем использовать её и в других методах. Или даже в других классах, обратившись к ней через Circle::PI.

Интерфейсы

Окей, разобрались с константами и имеем в итоге 3 класса, описывающих геометрические фигуры и реализацию для вычисления их площадей. Если присмотреться, то мы видим, что во всех классах определён метод calculateSquare(), возвращающий float. Можно сказать, что у них есть что-то общее.

Допустим, мы хотели бы, чтобы у нас были фигуры, которые умеют считать свою площадь. То есть, говоря чуть более абстрактно, какие-то наши классы обязаны реализовать какой-то внешний интерфейс, а именно – иметь метод calculateSquare(), который всегда возвращает float.
Для этой задачи в PHP есть интерфейсы. Это такие «контракты», которые класс должен соблюдать, если он на это «подписался». А говоря языком программистов, классы могут реализовывать интерфейсы.

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

interface CalculateSquare

Чтобы обязать класс реализовать этот интерфейс нужно использовать слово implements после имени класса.

class Circle implements CalculateSquare

Один класс может реализовывать сразу несколько интерфейсов, в таком случае они просто перечисляются через запятую.

class Circle implements CalculateSquare, Interface2, Interface3

Подсветка интерфейсов в PhpStorm

IDE PhpStorm автоматически понимает, что наш класс реализует интерфейс и рисует слева от методов специальные иконки. Если по ним кликнуть, то нас перекинет на интерфейс.

Реализации интерфейса

Ну и в интерфейсе если кликнуть на такую иконку, то нам откроется список мест, где этот интерфейс реализован.

Напоминание о необходимости реализации интерфейса

Если же мы напишем, что класс реализует какой-то интерфейс, но не реализуем его, то получим ошибку. Об этом нам даже подскажет IDE. Давайте удалим метод calculateSquare() из класса Circle. IDE любезно подчеркнёт красным строку, в которой мы говорим, что класс реализует интерфейс.

Ошибка при нереализованном интерфейсе

Если же мы попробуем запустить этот код, то и вовсе словим фатальную ошибку.

Так что давайте этот метод вернём обратно =)

Что ещё стоит сказать об интерфейсах – один интерфейс может содержать требования по реализации нескольких методов. Они просто перечисляются один за другим, вот так:
interface CalculateSquare

Но мы пока ограничимся одним методом calculateSquare().

Окей, так для чего это всё?

В программировании зачастую требуется проверить, что перед нами сейчас какой-то конкретный тип объектов, то есть что перед нами экземпляр какого-то класса, либо что этот объект реализует какой-то интерфейс. Для этого используется конструкция instanceof. С её помощью можно понять, является ли объект экземпляром какого-то класса, или реализует интерфейс. Эта конструкция возвращает true или false.

$circle1 = new Circle(2.5); var_dump($circle1 instanceof Circle);
boolean true

Всё верно, объект $circle1 является экземпляром класса Circle. Давайте теперь проверим, является ли он экземпляром класса Rectangle.

$circle1 = new Circle(2.5); var_dump($circle1 instanceof Rectangle);
boolean false

И снова всё верно, он не является экземпляром класса Rectangle.

А теперь давайте проверим, является ли он объектом, класс которого реализует интерфейс CalculateSquare.

$circle1 = new Circle(2.5); var_dump($circle1 instanceof CalculateSquare);
boolean true

Вуаля! Теперь мы перед тем как попросить какой-либо объект посчитать свою площадь, можем проверить, есть ли у него такой метод, то есть, реализует ли он соответствующий интерфейс.
Давайте добавим во все наши классы информацию о том, что они реализуют интерфейс CalculateSquare.

class Square implements CalculateSquare . class Rectangle implements CalculateSquare . class Circle implements CalculateSquare

Давайте теперь насоздаём объектов этих классов и положим их в массив:

$objects = [ new Square(5), new Rectangle(2, 4), new Circle(5) ];

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

$objects = [ new Square(5), new Rectangle(2, 4), new Circle(5) ]; foreach ($objects as $object) < if ($object instanceof CalculateSquare) < echo 'Объект реализует интерфейс CalculateSquare. Площадь: ' . $object->calculateSquare(); echo '
'; > >
Объект реализует интерфейс CalculateSquare. Площадь: 25 Объект реализует интерфейс CalculateSquare. Площадь: 8 Объект реализует интерфейс CalculateSquare. Площадь: 78.54

Давайте теперь уберём из класса Rectangle упоминание о том, что он реализует этот интерфейс.
class Rectangle

И снова попробуем запустить код.

Объект реализует интерфейс CalculateSquare. Площадь: 25 Объект реализует интерфейс CalculateSquare. Площадь: 78.54

Как видим, проверка успешно отработала и объект класса Rectangle был пропущен.

Полный код, полученный в ходе урока:

 class Circle implements CalculateSquare < const PI = 3.1416; private $r; public function __construct(float $r) < $this->r = $r; > public function calculateSquare(): float < return self::PI * ($this->r ** 2); > > class Rectangle < private $x; private $y; public function __construct(float $x, float $y) < $this->x = $x; $this->y = $y; > public function calculateSquare(): float < return $this->x * $this->y; > > class Square implements CalculateSquare < private $x; public function __construct(float $x) < $this->x = $x; > public function calculateSquare(): float < return $this->x ** 2; > > $objects = [ new Square(5), new Rectangle(2, 4), new Circle(5) ]; foreach ($objects as $object) < if ($object instanceof CalculateSquare) < echo 'Объект реализует интерфейс CalculateSquare. Площадь: ' . $object->calculateSquare(); echo '
'; > >

На этом с интерфейсами пока всё. В домашке будет ещё одна интересная функция, которая позволит вам узнать об объектах ещё кое-что.

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

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