mirror of
https://github.com/php/doc-ru.git
synced 2025-08-16 18:22:04 +00:00
510 lines
16 KiB
XML
510 lines
16 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
||
<!-- EN-Revision: c94529403818df20d843f8eee6407ab62f341025 Maintainer: mch Status: ready -->
|
||
<!-- Reviewed: yes -->
|
||
<!-- $Revision$ -->
|
||
<sect1 xml:id="language.oop5.traits" xmlns="http://docbook.org/ns/docbook">
|
||
<title>Трейты</title>
|
||
<para>
|
||
PHP реализует метод для повторного использования кода под названием трейт (trait).
|
||
</para>
|
||
<para>
|
||
Трейт - это механизм обеспечения повторного использования кода в языках с
|
||
поддержкой только одиночного наследования, таких как PHP. Трейт предназначен для уменьшения
|
||
некоторых ограничений одиночного наследования, позволяя разработчику повторно
|
||
использовать наборы методов свободно, в нескольких независимых классах и реализованных
|
||
с использованием разных архитектур построения классов. Семантика комбинации трейтов и
|
||
классов определена таким образом, чтобы снизить уровень сложности, а также избежать
|
||
типичных проблем, связанных с множественным наследованием и смешиванием (mixins).
|
||
</para>
|
||
<para>
|
||
Трейт очень похож на класс, но предназначен для группирования функционала хорошо
|
||
структурированным и последовательным образом. Невозможно создать самостоятельный
|
||
экземпляр трейта. Это дополнение к обычному наследованию и позволяет сделать
|
||
горизонтальную композицию поведения, то есть применение членов класса без
|
||
необходимости наследования.
|
||
</para>
|
||
|
||
<example xml:id="language.oop5.traits.basicexample">
|
||
<title>Пример использования трейта</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
trait ezcReflectionReturnInfo {
|
||
function getReturnType() { /*1*/ }
|
||
function getReturnDescription() { /*2*/ }
|
||
}
|
||
|
||
class ezcReflectionMethod extends ReflectionMethod {
|
||
use ezcReflectionReturnInfo;
|
||
/* ... */
|
||
}
|
||
|
||
class ezcReflectionFunction extends ReflectionFunction {
|
||
use ezcReflectionReturnInfo;
|
||
/* ... */
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
|
||
<sect2 xml:id="language.oop5.traits.precedence">
|
||
<title>Приоритет</title>
|
||
<para>
|
||
Наследуемый член из базового класса переопределяется членом, находящимся в трейте.
|
||
Порядок приоритета следующий: члены из текущего класса переопределяют методы в
|
||
трейте, которые в свою очередь переопределяют унаследованные методы.
|
||
</para>
|
||
<example xml:id="language.oop5.traits.precedence.examples.ex1">
|
||
<title>Пример приоритета старшинства</title>
|
||
<para>
|
||
Наследуемый метод от базового класса переопределяется методом, вставленным в
|
||
MyHelloWorld из трейта SayWorld. Поведение такое же как и для методов, определённых в
|
||
классе MyHelloWorld. Порядок приоритета такой: методы из текущего класса
|
||
переопределяют методы трейта, которые в свою очередь переопределяют методы из
|
||
базового класса.
|
||
</para>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
class Base {
|
||
public function sayHello() {
|
||
echo 'Hello ';
|
||
}
|
||
}
|
||
|
||
trait SayWorld {
|
||
public function sayHello() {
|
||
parent::sayHello();
|
||
echo 'World!';
|
||
}
|
||
}
|
||
|
||
class MyHelloWorld extends Base {
|
||
use SayWorld;
|
||
}
|
||
|
||
$o = new MyHelloWorld();
|
||
$o->sayHello();
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
Hello World!
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
<example xml:id="language.oop5.traits.precedence.examples.ex2">
|
||
<title>Пример альтернативного порядка приоритета</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
trait HelloWorld {
|
||
public function sayHello() {
|
||
echo 'Hello World!';
|
||
}
|
||
}
|
||
|
||
class TheWorldIsNotEnough {
|
||
use HelloWorld;
|
||
public function sayHello() {
|
||
echo 'Hello Universe!';
|
||
}
|
||
}
|
||
|
||
$o = new TheWorldIsNotEnough();
|
||
$o->sayHello();
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
Hello Universe!
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
</sect2>
|
||
|
||
<sect2 xml:id="language.oop5.traits.multiple">
|
||
<title>Несколько трейтов</title>
|
||
<para>
|
||
Несколько трейтов могут быть вставлены в класс путём их перечисления в
|
||
директиве use, разделяя запятыми.
|
||
</para>
|
||
<example xml:id="language.oop5.traits.multiple.ex1">
|
||
<title>Пример использования нескольких трейтов</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
trait Hello {
|
||
public function sayHello() {
|
||
echo 'Hello ';
|
||
}
|
||
}
|
||
|
||
trait World {
|
||
public function sayWorld() {
|
||
echo 'World';
|
||
}
|
||
}
|
||
|
||
class MyHelloWorld {
|
||
use Hello, World;
|
||
public function sayExclamationMark() {
|
||
echo '!';
|
||
}
|
||
}
|
||
|
||
$o = new MyHelloWorld();
|
||
$o->sayHello();
|
||
$o->sayWorld();
|
||
$o->sayExclamationMark();
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
Hello World!
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
</sect2>
|
||
|
||
<sect2 xml:id="language.oop5.traits.conflict">
|
||
<title>Разрешение конфликтов</title>
|
||
<para>
|
||
Если два трейта вставляют метод с одним и тем же именем, это приводит к фатальной
|
||
ошибке в случае, если конфликт явно не разрешён.
|
||
</para>
|
||
<para>
|
||
Для разрешения конфликтов именования между трейтами, используемыми в одном и том же
|
||
классе, необходимо использовать оператор <literal>insteadof</literal> для того, чтобы точно
|
||
выбрать один из конфликтующих методов.
|
||
</para>
|
||
<para>
|
||
Так как предыдущий оператор позволяет только исключать методы, оператор
|
||
<literal>as</literal> может быть использован для включения одного из конфликтующих
|
||
методов под другим именем. Обратите внимание, что оператор <literal>as</literal> не
|
||
переименовывает метод и не влияет на какой-либо другой метод.
|
||
</para>
|
||
<example xml:id="language.oop5.traits.conflict.ex1">
|
||
<title>Пример разрешения конфликтов</title>
|
||
<para>
|
||
В этом примере Talker использует трейты A и B.
|
||
Так как в A и B есть конфликтующие методы, он определяет использовать
|
||
вариант smallTalk из трейта B, а вариант bigTalk - из трейта A.
|
||
</para>
|
||
<para>
|
||
Класс Aliased_Talker применяет оператор <literal>as</literal>
|
||
чтобы получить возможность использовать имплементацию bigTalk из B
|
||
под дополнительным псевдонимом <literal>talk</literal>.
|
||
</para>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
trait A {
|
||
public function smallTalk() {
|
||
echo 'a';
|
||
}
|
||
public function bigTalk() {
|
||
echo 'A';
|
||
}
|
||
}
|
||
|
||
trait B {
|
||
public function smallTalk() {
|
||
echo 'b';
|
||
}
|
||
public function bigTalk() {
|
||
echo 'B';
|
||
}
|
||
}
|
||
|
||
class Talker {
|
||
use A, B {
|
||
B::smallTalk insteadof A;
|
||
A::bigTalk insteadof B;
|
||
}
|
||
}
|
||
|
||
class Aliased_Talker {
|
||
use A, B {
|
||
B::smallTalk insteadof A;
|
||
A::bigTalk insteadof B;
|
||
B::bigTalk as talk;
|
||
}
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
</sect2>
|
||
|
||
<sect2 xml:id="language.oop5.traits.visibility">
|
||
<title>Изменение видимости метода</title>
|
||
<para>
|
||
Используя синтаксис оператора <literal>as</literal> можно также настроить
|
||
видимость метода в использующем трейт классе.
|
||
</para>
|
||
<example xml:id="language.oop5.traits.visibility.ex1">
|
||
<title>Пример изменения видимости метода</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
trait HelloWorld {
|
||
public function sayHello() {
|
||
echo 'Hello World!';
|
||
}
|
||
}
|
||
|
||
// Изменение видимости метода sayHello
|
||
class MyClass1 {
|
||
use HelloWorld { sayHello as protected; }
|
||
}
|
||
|
||
// Создание псевдонима метода с изменённой видимостью
|
||
// видимость sayHello не изменилась
|
||
class MyClass2 {
|
||
use HelloWorld { sayHello as private myPrivateHello; }
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
</sect2>
|
||
|
||
<sect2 xml:id="language.oop5.traits.composition">
|
||
<title>Трейты, состоящие из трейтов</title>
|
||
<para>
|
||
Аналогично тому, как классы могут использовать трейты, также и трейты могут использовать
|
||
другие трейты. Используя один или более трейтов в определении другого трейта, он может
|
||
частично или полностью состоять из членов, определённых в этих трейтах.
|
||
</para>
|
||
<example xml:id="language.oop5.traits.composition.ex1">
|
||
<title>Пример трейтов, составленных из трейтов</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
trait Hello {
|
||
public function sayHello() {
|
||
echo 'Hello ';
|
||
}
|
||
}
|
||
|
||
trait World {
|
||
public function sayWorld() {
|
||
echo 'World!';
|
||
}
|
||
}
|
||
|
||
trait HelloWorld {
|
||
use Hello, World;
|
||
}
|
||
|
||
class MyHelloWorld {
|
||
use HelloWorld;
|
||
}
|
||
|
||
$o = new MyHelloWorld();
|
||
$o->sayHello();
|
||
$o->sayWorld();
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
Hello World!
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
</sect2>
|
||
|
||
<sect2 xml:id="language.oop5.traits.abstract">
|
||
<title>Абстрактные члены трейтов</title>
|
||
<para>
|
||
Трейты поддерживают использование абстрактных методов для того, чтобы установить требования
|
||
к использующему классу.
|
||
Поддерживаются общедоступные, защищённые и закрытые методы.
|
||
До PHP 8.0.0 поддерживались только общедоступные и защищённые абстрактные методы.
|
||
</para>
|
||
<caution>
|
||
<simpara>
|
||
Конкретный класс исполняет эти требования путём определения конкретного
|
||
метода с тем же именем; при этом сигнатура метода может отличаться.
|
||
</simpara>
|
||
</caution>
|
||
<example xml:id="language.oop5.traits.abstract.ex1">
|
||
<title>Требования трейта при помощи абстрактных методов</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
trait Hello {
|
||
public function sayHelloWorld() {
|
||
echo 'Hello'.$this->getWorld();
|
||
}
|
||
abstract public function getWorld();
|
||
}
|
||
|
||
class MyHelloWorld {
|
||
private $world;
|
||
use Hello;
|
||
public function getWorld() {
|
||
return $this->world;
|
||
}
|
||
public function setWorld($val) {
|
||
$this->world = $val;
|
||
}
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
</sect2>
|
||
|
||
<sect2 xml:id="language.oop5.traits.static">
|
||
<title>Статические члены трейта</title>
|
||
<para>
|
||
В трейтах можно определять статические переменные, статические методы и статические свойства.
|
||
</para>
|
||
<example xml:id="language.oop5.traits.static.ex1">
|
||
<title>Статические переменные</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
trait Counter {
|
||
public function inc() {
|
||
static $c = 0;
|
||
$c = $c + 1;
|
||
echo "$c\n";
|
||
}
|
||
}
|
||
|
||
class C1 {
|
||
use Counter;
|
||
}
|
||
|
||
class C2 {
|
||
use Counter;
|
||
}
|
||
|
||
$o = new C1(); $o->inc(); // echo 1
|
||
$p = new C2(); $p->inc(); // echo 1
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.traits.static.ex2">
|
||
<title>Статические методы</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
trait StaticExample {
|
||
public static function doSomething() {
|
||
return 'Что-либо делаем';
|
||
}
|
||
}
|
||
|
||
class Example {
|
||
use StaticExample;
|
||
}
|
||
|
||
Example::doSomething();
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.traits.static.ex3">
|
||
<title>Статические свойства</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
trait StaticExample {
|
||
public static $static = 'foo';
|
||
}
|
||
class Example {
|
||
use StaticExample;
|
||
}
|
||
echo Example::$static;
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
</sect2>
|
||
|
||
<sect2 xml:id="language.oop5.traits.properties">
|
||
<title>Свойства</title>
|
||
<para>
|
||
Трейты могут также определять свойства.
|
||
</para>
|
||
<example xml:id="language.oop5.traits.properties.example">
|
||
<title>Определение свойств</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
trait PropertiesTrait {
|
||
public $x = 1;
|
||
}
|
||
|
||
class PropertiesExample {
|
||
use PropertiesTrait;
|
||
}
|
||
|
||
$example = new PropertiesExample;
|
||
$example->x;
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<para>
|
||
Если трейт определяет свойство, то класс не может определить свойство с
|
||
таким же именем, кроме случаев полного совпадения (те же начальное значение и
|
||
модификатор видимости), иначе будет сгенерирована фатальная ошибка.
|
||
</para>
|
||
<example xml:id="language.oop5.traits.properties.conflicts">
|
||
<title>Разрешение конфликтов</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
trait PropertiesTrait {
|
||
public $same = true;
|
||
public $different = false;
|
||
}
|
||
|
||
class PropertiesExample {
|
||
use PropertiesTrait;
|
||
public $same = true;
|
||
public $different = true; // Фатальная ошибка
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
</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
|
||
-->
|