mirror of
https://github.com/php/doc-ru.git
synced 2026-04-27 01:08:05 +02:00
570 lines
17 KiB
XML
570 lines
17 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- EN-Revision: 7341710fa635b4a969bc855a1e4619cdfa6b63f1 Maintainer: mch Status: ready -->
|
|
<!-- Reviewed: no -->
|
|
<sect1 xml:id="language.oop5.traits" xmlns="http://docbook.org/ns/docbook">
|
|
<title>Трейты</title>
|
|
<para>
|
|
PHP реализует способ переиспользования кода, называемый трейтами (Traits).
|
|
</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>
|
|
В класс можно добавить несколько трейтов, перечислив их в директиве
|
|
<literal>use</literal> через запятую.
|
|
</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>
|
|
Начиная с PHP 8.0.0 сигнатура конкретного метода должна следовать
|
|
<link linkend="language.oop.lsp">правилам совместимости сигнатур</link>.
|
|
Ранее сигнатура метода могла несовпадать.
|
|
</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>
|
|
<note>
|
|
<para>
|
|
Начиная с PHP 8.1.0 прямой вызов статического метода
|
|
или прямой доступ к статическому свойству в трейте устарел.
|
|
К статическим методам и свойствам нужно обращаться только в классе, в который включён трейт.
|
|
</para>
|
|
</note>
|
|
<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() {
|
|
echo 'Что-либо делаем';
|
|
}
|
|
}
|
|
|
|
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>
|
|
Если трейт определяет свойство, то класс не может определить свойство
|
|
c таким же именем, кроме случаев полного совпадения (та же область видимости и тип,
|
|
модификатор readonly и начальное значение), иначе будет выброшена фатальная ошибка.
|
|
</para>
|
|
<example xml:id="language.oop5.traits.properties.conflicts">
|
|
<title>Разрешение конфликтов</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait PropertiesTrait {
|
|
public $same = true;
|
|
public $different1 = false;
|
|
public bool $different2;
|
|
public bool $different3;
|
|
}
|
|
|
|
class PropertiesExample {
|
|
use PropertiesTrait;
|
|
public $same = true;
|
|
public $different1 = true; // Фатальная ошибка
|
|
public string $different2; // Фатальная ошибка
|
|
readonly protected bool $different3; // Фатальная ошибка
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="language.oop5.traits.constants">
|
|
<title>&Constants;</title>
|
|
<para>
|
|
Начиная с версии PHP 8.2.0 трейты могут также определять константы.
|
|
</para>
|
|
<example xml:id="language.oop5.traits.constants.example">
|
|
<title>Определение констант</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait ConstantsTrait {
|
|
public const FLAG_MUTABLE = 1;
|
|
final public const FLAG_IMMUTABLE = 5;
|
|
}
|
|
|
|
class ConstantsExample {
|
|
use ConstantsTrait;
|
|
}
|
|
|
|
$example = new ConstantsExample;
|
|
echo $example::FLAG_MUTABLE; // 1
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<para>
|
|
Если трейт определяет константу, то класс не может определить константу с таким же именем,
|
|
если только они не совместимы (одинаковая область видимости, начальное значение и модификатор final),
|
|
иначе выбрасывается фатальная ошибка.
|
|
</para>
|
|
<example xml:id="language.oop5.traits.constants.conflicts">
|
|
<title>Разрешение конфликтов</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait ConstantsTrait {
|
|
public const FLAG_MUTABLE = 1;
|
|
final public const FLAG_IMMUTABLE = 5;
|
|
}
|
|
|
|
class ConstantsExample {
|
|
use ConstantsTrait;
|
|
public const FLAG_IMMUTABLE = 5; // Фатальная ошибка
|
|
}
|
|
?>
|
|
]]>
|
|
</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
|
|
-->
|