mirror of
https://github.com/php/doc-ru.git
synced 2026-03-24 07:42:22 +01:00
* Update interfaces.xml to en * Update construct.xml to en * Update construct.xml * Update construct.xml to en
479 lines
18 KiB
XML
479 lines
18 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
||
<!-- EN-Revision: 565bd8b6cf2cae44ae2bc54ef6dbe6ee70ddfefd Maintainer: shein Status: ready -->
|
||
<!-- Reviewed: no -->
|
||
<sect1 xml:id="language.oop5.interfaces" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||
<title>Интерфейсы объектов</title>
|
||
<para>
|
||
Интерфейсы объектов разрешают создавать код, который
|
||
указывает, какие методы и свойства требуется реализовать в классе,
|
||
без определения реализации этих методов или свойств.
|
||
Интерфейсы разделяют пространство имён с классами, трейтами
|
||
и перечислениями, поэтому им нельзя давать одинаковые названия.
|
||
</para>
|
||
<para>
|
||
Интерфейсы определяются так же, как классы, но с ключевым словом
|
||
<literal>interface</literal> вместо слова <literal>class</literal>
|
||
и с методами, ни один из которых не определяет содержимое тела.
|
||
</para>
|
||
<para>
|
||
Методы интерфейса объявляются общедоступными,
|
||
что вытекает из самой природы интерфейса.
|
||
</para>
|
||
<para>
|
||
Интерфейсы преследуют две взаимодополняющие цели:
|
||
</para>
|
||
<simplelist>
|
||
<member>
|
||
Разрешают разработчикам создавать объекты разноимённых классов, которые умеют взаимно заменять друг друга,
|
||
поскольку реализуют один и тот же интерфейс или интерфейсы.
|
||
Интерфейсы часто внедряют в код, когда требуется создать набор служб доступа к базе данных,
|
||
платёжных шлюзов или стратегий кеширования. Один класс подменяют другим без изменения кода, который его использует.
|
||
</member>
|
||
<member>
|
||
Разрешают параметру функции или метода принимать и обрабатывать объект, который подчиняется контракту интерфейса,
|
||
чтобы не заботиться о том, что ещё умеет делать объект или как его реализовали.
|
||
Интерфейсы часто называют <literal>Iterable</literal>, <literal>Cacheable</literal>, <literal>Renderable</literal>
|
||
и другими похожими именами, чтобы описать поведение интерфейса.
|
||
</member>
|
||
</simplelist>
|
||
<para>
|
||
В интерфейсах также определяют
|
||
<link linkend="language.oop5.magic">магические методы</link>, чтобы потребовать от классов,
|
||
которые реализуют интерфейс, реализации этих методов.
|
||
</para>
|
||
|
||
<note>
|
||
<para>
|
||
Лучше не включать <link linkend="language.oop5.decon.constructor">конструкторы</link> в интерфейсы,
|
||
чтобы не снижать гибкость объекта, который реализует интерфейс,
|
||
хотя включение конструкторов и поддерживается.
|
||
Конструкторы, кроме того, не соблюдают правила наследования, из-за чего поведение иногда становится противоречивым
|
||
и неожиданным.
|
||
</para>
|
||
</note>
|
||
|
||
<sect2 xml:id="language.oop5.interfaces.implements">
|
||
<title><literal>Оператор implements</literal></title>
|
||
<para>
|
||
Для реализации интерфейса используется оператор <literal>implements</literal>.
|
||
Класс должен реализовать все методы, описанные в интерфейсе, иначе
|
||
произойдёт фатальная ошибка. При желании классы могут реализовывать
|
||
более одного интерфейса, разделяя каждый интерфейс запятой.
|
||
</para>
|
||
<warning>
|
||
<para>
|
||
Параметрам в методах класса, в котором реализуется интерфейс, разрешается указывать названия,
|
||
которые не совпадают с названиями параметров в методах интерфейса. Но начиная с PHP 8.0 язык поддерживает
|
||
<link linkend="functions.named-arguments">именованные аргументы</link>, и код, в котором вызываются методы интерфейса,
|
||
часто полагается на названия параметров в интерфейсе.
|
||
Поэтому разработчикам рекомендуют указывать в методах те же названия параметров,
|
||
что и в реализуемом интерфейсе.
|
||
</para>
|
||
</warning>
|
||
<note>
|
||
<para>
|
||
Аналогично классам, интерфейсы расширяют оператором
|
||
<link linkend="language.oop5.inheritance">extends</link>.
|
||
</para>
|
||
</note>
|
||
<note>
|
||
<para>
|
||
Класс, которым реализуется интерфейс, обязан объявить каждый метод интерфейса
|
||
<link linkend="language.oop.lsp">по правилам совместимости сигнатур</link>.
|
||
Реализация методов обязана следовать <link linkend="language.oop.lsp">правилам совместимости сигнатур</link>
|
||
для каждого интерфейса, когда класс реализует больше одного интерфейса,
|
||
в котором объявили методы с одинаковым названием. Поэтому при организации иерархии типов PHP-разработчики
|
||
пользуются доступной в языке <link linkend="language.oop5.variance">ковариантностью и контравариантностью</link>.
|
||
</para>
|
||
</note>
|
||
</sect2>
|
||
<!-- Move this to OOP constants page? -->
|
||
<sect2 xml:id="language.oop5.interfaces.constants">
|
||
<title>Константы</title>
|
||
<para>
|
||
Интерфейсы поддерживают объявления констант. Константы интерфейсов
|
||
работают так же, как <link linkend="language.oop5.constants">константы классов</link>.
|
||
До PHP 8.1.0 константы интерфейса нельзя было переопределять в производном классе или интерфейсе.
|
||
</para>
|
||
</sect2>
|
||
<sect2 xml:id="language.oop5.interfaces.properties">
|
||
<title>Properties</title>
|
||
<simpara>
|
||
Начиная с PHP 8.4.0 в интерфейсах разрешили объявлять свойства.
|
||
При объявлении свойств потребуется указать, доступно ли свойство для чтения,
|
||
записи или и того, и другого.
|
||
Объявление интерфейса применяется только к открытому доступу на чтение и запись.
|
||
</simpara>
|
||
<simpara>
|
||
Класс удовлетворяет свойству интерфейса несколькими способами:
|
||
класс определяет открытое свойство,
|
||
<link linkend="language.oop5.property-hooks.virtual">виртуальное свойство</link>,
|
||
которое реализует только тот хук, который соответствует хуку интерфейса,
|
||
или определяет <literal>readonly</literal>-свойство, которое удовлетворяет свойству интерфейса для чтения.
|
||
Однако в классе нельзя ограничивать доступ на запись свойства модификатором <literal>readonly</literal>,
|
||
если в интерфейсе свойство объявили доступным для записи.
|
||
</simpara>
|
||
<example>
|
||
<title>Пример свойств интерфейса</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?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);
|
||
}
|
||
}
|
||
}
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
</sect2>
|
||
<sect2 xml:id="language.oop5.interfaces.examples">
|
||
&reftitle.examples;
|
||
<example xml:id="language.oop5.interfaces.examples.ex1">
|
||
<title>Пример интерфейса</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?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;
|
||
}
|
||
}
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.interfaces.examples.ex2">
|
||
<title>Наследование интерфейсов</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?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) {}
|
||
}
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.interfaces.examples.variance.multiple.interfaces">
|
||
<title>Совместимость с несколькими интерфейсами</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?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();
|
||
}
|
||
}
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.interfaces.examples.ex3">
|
||
<title>Множественное наследование интерфейсов</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?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() {}
|
||
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.interfaces.examples.ex4">
|
||
<title>Интерфейсы с константами</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
interface A
|
||
{
|
||
const B = 'Константа интерфейса';
|
||
}
|
||
|
||
// Выведет: Константа интерфейса
|
||
echo A::B;
|
||
|
||
|
||
class B implements A
|
||
{
|
||
const B = 'Константа класса';
|
||
}
|
||
|
||
// Выведет: Константа класса
|
||
// До PHP 8.1.0 этот код не сработает,
|
||
// поскольку запрещалось переопределять константы
|
||
echo B::B;
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.interfaces.examples.ex5">
|
||
<title>Интерфейсы с абстрактными классами</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?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;
|
||
}
|
||
}
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.interfaces.examples.ex6">
|
||
<title>Одновременное расширение класса и реализация интерфейсов</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
class One
|
||
{
|
||
/* ... */
|
||
}
|
||
|
||
interface Usable
|
||
{
|
||
/* ... */
|
||
}
|
||
|
||
interface Updatable
|
||
{
|
||
/* ... */
|
||
}
|
||
|
||
// Порядок ключевых слов здесь важен. Слово extends должно идти первым
|
||
class Two extends One implements
|
||
Usable,
|
||
Updatable
|
||
{
|
||
/* ... */
|
||
}
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<para>
|
||
Интерфейс вместе с объявлениями типов предоставляет надёжный
|
||
способ проверки того, что конкретный объект содержит конкретные
|
||
методы. Смотрите также описание оператора
|
||
<link linkend="language.operators.type">instanceof</link>
|
||
и раздел «<link linkend="language.types.declarations">Объявления типов</link>».
|
||
</para>
|
||
</sect2>
|
||
|
||
</sect1><!-- Keep this comment at the end of the file
|
||
Local variables:
|
||
mode: sgml
|
||
sgml-omittag:t
|
||
sgml-shorttag:t
|
||
sgml-minimize-attributes:nil
|
||
sgml-always-quote-attributes:t
|
||
sgml-indent-step:1
|
||
sgml-indent-data:t
|
||
indent-tabs-mode:nil
|
||
sgml-parent-document:nil
|
||
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
||
sgml-exposed-tags:nil
|
||
sgml-local-catalogs:nil
|
||
sgml-local-ecat-files:nil
|
||
End:
|
||
vim600: syn=xml fen fdm=syntax fdl=2 si
|
||
vim: et tw=78 syn=sgml
|
||
vi: ts=1 sw=1
|
||
-->
|