PHP реализует способ повторного использования кода под названием Traits.
Трейты — это механизм повторного использования кода в языках одиночного наследования, таких как PHP. Черта предназначена для уменьшения некоторых ограничений одиночного наследования, позволяя разработчику свободно повторно использовать наборы методов в нескольких независимых классах, живущих в разных иерархиях классов. Семантика комбинации трейтов и классов определяется таким образом, чтобы уменьшить сложность и избежать типичных проблем, связанных с множественным наследованием и примесями.
Черта аналогична классу, но предназначена только для группировки функций в мелкозернистой и согласованной форме. Невозможно создать экземпляр черты сам по себе. Это дополнение к традиционному наследованию, которое обеспечивает горизонтальную композицию поведения; то есть применение членов класса без необходимости наследования.
Пример #1 Пример признака
<?php
trait ezcReflectionReturnInfo {
function getReturnType() { /*1*/ }
function getReturnDescription() { /*2*/ }
}
class ezcReflectionMethod extends ReflectionMethod {
use ezcReflectionReturnInfo;
/* ... */
}
class ezcReflectionFunction extends ReflectionFunction {
use ezcReflectionReturnInfo;
/* ... */
}
?>
Приоритет
Унаследованный член от базового класса переопределяется членом, вставленным признаком. Порядок приоритета заключается в том, что члены текущего класса переопределяют методы Trait, которые, в свою очередь, переопределяют унаследованные методы.
Пример #2 Пример приоритетного порядка
Унаследованный метод от базового класса переопределяется методом, вставленным в MyHelloWorld из черты SayWorld. Поведение такое же для методов, определенных в классе MyHelloWorld. Порядок приоритета заключается в том, что методы текущего класса переопределяют методы Trait, которые, в свою очередь, переопределяют методы базового класса.
<?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();
?>
Приведенный выше пример выведет:
Привет, мир!
Пример #3 Пример альтернативного порядка приоритета
<?php
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}
class TheWorldIsNotEnough {
use HelloWorld;
public function sayHello() {
echo 'Hello Universe!';
}
}
$o = new TheWorldIsNotEnough();
$o->sayHello();
?>
Приведенный выше пример выведет:
Здравствуй Вселенная!
Несколько признаков
В класс можно вставить несколько признаков, перечислив их в use
операторе, разделенных запятыми.
Пример #4 Использование нескольких трейтов
<?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();
?>
Приведенный выше пример выведет:
Привет, мир!
Разрешение конфликтов
Если два трейта вставляют метод с одинаковым именем, возникает фатальная ошибка, если конфликт явно не разрешен.
Чтобы разрешить конфликты имен между трейтами, используемыми в одном и том же классе, insteadof
необходимо использовать оператор для выбора ровно одного из конфликтующих методов.
Поскольку это позволяет исключать только методы, as
оператор можно использовать для добавления псевдонима к одному из методов. Обратите внимание, что as
оператор не переименовывает метод и не влияет ни на какой другой метод.
Пример #5 Разрешение конфликта
В этом примере Talker использует черты A и B. Поскольку A и B имеют конфликтующие методы, он определяет использование варианта smallTalk из черты B и варианта bigTalk из черты A.
Aliased_Talker использует as
оператор, чтобы иметь возможность использовать реализацию bigTalk B под дополнительным псевдонимом talk
.
<?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;
}
}
?>
Изменение видимости метода
Используя as
синтаксис, можно также настроить видимость метода в экспонирующем классе.
Пример #6 Изменение видимости метода
<?php
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}
// Change visibility of sayHello
class MyClass1 {
use HelloWorld { sayHello as protected; }
}
// Alias method with changed visibility
// sayHello visibility not changed
class MyClass2 {
use HelloWorld { sayHello as private myPrivateHello; }
}
?>
Трейты, составленные из трейтов
Точно так же, как классы могут использовать черты, так же могут использовать и другие черты. Используя один или несколько признаков в определении признака, он может быть частично или полностью составлен из членов, определенных в этих других признаках.
Пример #7 Трейты, составленные из трейтов
<?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();
?>
Приведенный выше пример выведет:
Привет, мир!
Члены абстрактных признаков
Трейты поддерживают использование абстрактных методов для наложения требований на демонстрирующий класс. Поддерживаются общедоступные, защищенные и частные методы. До PHP 8.0.0 поддерживались только общедоступные и защищенные абстрактные методы.
Осторожность
Конкретный класс выполняет это требование, определяя конкретный метод с тем же именем; его подпись может быть другой.
Пример #8. Экспресс-требования с помощью абстрактных методов
<?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;
}
}
?>
Статические трейты-члены
Черты могут определять статические переменные, статические методы и статические свойства.
Примечание :
Начиная с PHP 8.1.0, вызов статического метода или доступ к статическому свойству непосредственно в трейте устарели. К статическим методам и свойствам следует обращаться только в классе, используя трейт.
Пример #9 Статические переменные
<?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
?>
Пример #10 Статические методы
<?php
trait StaticExample {
public static function doSomething() {
return 'Doing something';
}
}
class Example {
use StaticExample;
}
Example::doSomething();
?>
Пример #11 Статические свойства
<?php
trait StaticExample {
public static $static = 'foo';
}
class Example {
use StaticExample;
}
echo Example::$static;
?>
Свойства
Черты также могут определять свойства.
Пример #12 Определение свойств
<?php
trait PropertiesTrait {
public $x = 1;
}
class PropertiesExample {
use PropertiesTrait;
}
$example = new PropertiesExample;
$example->x;
?>
Если трейт определяет свойство, то класс не может определить свойство с тем же именем, если оно не совместимо (такая же видимость и начальное значение), в противном случае выдается фатальная ошибка.
Пример #13 Разрешение конфликтов
<?php
trait PropertiesTrait {
public $same = true;
public $different = false;
}
class PropertiesExample {
use PropertiesTrait;
public $same = true;
public $different = true; // Fatal error
}
?>
0 комментариев