Что такое traits php

Содержание
  1. Трейты в PHP
  2. Traits в php 5.4. Разбираем детали реализации
  3. Синтаксис
  4. Свойства в типажах
  5. Область видимости
  6. Статические методы и свойства
  7. Совпадение методов типажей между собой и с методами класса
  8. Совпадение свойств типажа со свойствами другого типажа и свойствами класса
  9. Ошибки и исключения в типажах
  10. Немного белой чёрной магии
  11. Удаление метода типажа
  12. «Наследование» в типажах
  13. Два способа реализовать Singleton с помощью типажей
  14. Трейты
  15. Приоритет
  16. Несколько трейтов
  17. Разрешение конфликтов
  18. Изменение видимости метода
  19. Трейты, скомпонованные из трейтов
  20. Абстрактные члены трейтов
  21. Статические члены трейта
  22. Свойства
  23. Трейты в PHP
  24. Что такое трейты?
  25. Синтаксис
  26. Синтаксис
  27. Пример
  28. Использование нескольких трейтов
  29. Пример
  30. Чем трейты отличаются от интерфейсов?
  31. Пример
  32. Переопределение унаследованных методов
  33. Пример
  34. Использование трейтов в PHP
  35. Что представляет трейт
  36. Использование нескольких трейтов
  37. Комментарии ( 0 ):

Трейты в PHP

Как мы знаем, в PHP класс может наследоваться только от одного класса. Но как быть, если мы хотим иметь какой-либо функционал в разных классах, которые не являются наследниками друг друга? Для этого придумали трейты. Трейты в PHP – это такой механизм, который позволяет внутри классов избегать повторного использования кода.

Давайте разберём на примере. Пусть у нас будут два совершенно не связанных между собой класса: коробка и человек. Но предположим, что мы хотели бы, чтобы они оба умели говорить о том, какого они класса. Давайте для начала создадим два этих класса и прямо в них создадим методы sayYourClass(), которые будут выводить имя класса, объектами которого они являются.

В PHP можно получить имя класса с помощью конструкции ИмяКласса::class. Например:

Если мы находимся внутри класса, например, в каком-то его методе, то мы можем ИмяКласса заменить словом self – текущий класс.

Результат останется прежним.

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

Ничего сложного. Теперь мы можем просто использовать этот трейт в двух наших классах. Для этого используется конструкция use.

И снова увидим нужный результат:

Код из трейта SayYourClass просто подставился в классы, где мы его использовали с помощью слова use. В self будет лежать класс, в котором сейчас исполняется этот код. Вот так всё просто.

Трейты также довольно плотно пересекаются с темой интерфейсов.

Давайте добавим интерфейс, который будет обязывать классы иметь метод sayYourClass().

Теперь наши классы могут его реализовать – этот метод реализован в трейте, а трейт используется в классе.

Давайте посмотрим на получившийся код.

В PHP класс может наследоваться только от одного класса, помните? Так вот с помощью интерфейсов и трейтов мы можем это ограничение немного обойти. Мы теперь можем добавлять некоторый функционал в классы, которые не имеют какого-то общего поведения в целом. Но они при этом объединены одним интерфейсом. А проверять, реализует ли объект интерфейс, мы уже умеем – с помощью конструкции instanceof.

Таким образом, мы можем обходить ограничения наследования:

Источник

Traits в php 5.4. Разбираем детали реализации

Совсем недавно вышла первая beta php 5.4, а пока я писал топик подоспела и вторая. Одно из нововведений в 5.4 – это traits (типажи). Предлагаю разобраться во всех деталях в том, что же типажи из себя представляют в php.

Но во всём есть свои детали.

Синтаксис

В общем и целом всё просто. Типажей можно подключить к классу неограниченное кол-во через одну или несколько конструкций use внутри определения класса. use может быть указан в любом месте класса.

Более сложный пример:

Тут важно обратить внимание на два момента. Во-первых, блок после use кажется связанным с типажом около которого он описан, но это не так. Правила в блоке глобальные и могут быть объявлены в любом месте.

Типажи инициализируются, как и классы, динамически. При большом желании можно писать так:

Свойства в типажах

Область видимости

Статические методы и свойства

В типаже можно объявлять статические методы, но нельзя объявлять статические свойства. Внутри статических методов можно использовать, как статическое связывание (self::), так и динамическое (static::), всё будет работать так, как будто вызвано из метода класса («copy-paste»).

Ограничение на хранение статических свойств обойти можно, как именно покажу позже с обращением к магии.

Совпадение методов типажей между собой и с методами класса

Метод описанный в классе перекрывает метод из типажа. Но если какой-то метод описан в родительском классе, а в дочернем классе подключён типаж с таким же методом, он перекроет метод из родительского (снова вспоминаем «copy-paste»).

Хитрая ошибка может быть в случае, когда в классе тоже определён метод, вызвавший коллизию, в таком случае php пропустит эту проверку, т.к. он проверяет только «выжившие» методы типажа:Когда-нибудь потом, перенеся метод abc в родительский класс, получим странную ошибку по коллизии методов типажей, которая может сбить с толку. Так что, коллизии лучше разрешить заранее. (С другой стороны, если в коде методы типажа и класса совпадают, возможно что-то уже не так.)

Совпадение свойств типажа со свойствами другого типажа и свойствами класса

В этом моменте нас поджидают неприятные проблемы. Сразу пример:
Поясняю. В общем случае при пересечении свойств типажей между собой или свойств типажа и класса выдаётся ошибка. Но зачем-то для «совместимых» свойств делается исключение и они работают по принципу «кто последний, тот и прав». Поэтому в классе A в getId получилось NULL, а в классе B – false. При этом свойства класса считаются ниже, чем свойство типажа (с методами равно наоборот) и в C вместо ожидаемого ‘0’ получим false.

Совместимыми считаются значения нестрогое сравнение которых даёт true, а так как в php при этом много неявных преобразований, могут быть неприятные ошибки при использовании строго сравнения возвращаемых значений.

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

Ошибки и исключения в типажах

Если следовать мнемоническому правилу trait == «copy-paste», с ошибками становится сразу всё понятно:
Объект уже не знает, откуда у него взялся метод в котором был Notice или Exception, но это можно узнать в stack trace по строкам кода, в которых были вызовы. Если хранить типажи в отдельных файлах определить будет ещё проще.

Немного белой чёрной магии

Покажу пару грязных приёмов с типажами, используйте их на свой страх и риск.

Удаление метода типажа

Чтобы удалить метод типажа, например, когда ему был задан alias, можно сделать так:
Но в таком подходе таится большая опасность, т.к. одни методы типажа потенциально могут вызывать другие методы:
При переименовании типаж ничего не знает о том, что метод был переименован. Поэтому по-умолчанию при указании alias’а сохраняется оригинальный метод.

«Наследование» в типажах

С помощью похожего трюка можно реализовать «наследование» в типажах c возможностью вызова «родительских» методов.

Два способа реализовать Singleton с помощью типажей

Чтобы сгладить это магическое безобразие покажу один полезный пример. Часто в виде типажа приводят Singleton, хотя без возможности задания в типаже статической переменной сделать его будет не так просто, как кажется на первый взгляд. Можно воспользоваться двумя хитростями.

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

Источник

Трейты

Начиная с версии 5.4.0 PHP вводит инструментарий для повторного использования кода, называемый трейтом.

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

Пример #1 Пример использования трейта

trait ezcReflectionReturnInfo <
function getReturnType () < /*1*/ >
function getReturnDescription () < /*2*/ >
>

Приоритет

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

Пример #2 Пример приоритета старшинства

Наследуемый метод от базового класса переопределяется методом, вставленным в MyHelloWorld из трейта Trait. Поведение такое же как и для методов, определенных в классе MyHelloWorld. Порядок приоритета такой: методы из текущего класса переопределяют методы трейта, которые в свою очередь переопределяют методы из базового класса.

class Base <
public function sayHello () <
echo ‘Hello ‘ ;
>
>

trait SayWorld <
public function sayHello () <
parent :: sayHello ();
echo ‘World!’ ;
>
>

class MyHelloWorld extends Base <
use SayWorld ;
>

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

Пример #3 Пример альтернативного порядка приоритета

trait HelloWorld <
public function sayHello () <
echo ‘Hello World!’ ;
>
>

class TheWorldIsNotEnough <
use HelloWorld ;
public function sayHello () <
echo ‘Hello Universe!’ ;
>
>

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

Несколько трейтов

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

Пример #4 Пример использования нескольких трейтов

trait Hello <
public function sayHello () <
echo ‘Hello ‘ ;
>
>

trait World <
public function sayWorld () <
echo ‘World’ ;
>
>

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

Разрешение конфликтов

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

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

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

Пример #5 Пример разрешения конфликтов

В этом примере Talker использует трейты A и B. Так как в A и B есть конфликтные методы, он определяет использовать вариант smallTalk из трейта B, и вариант bigTalk из трейта A.

Класс Aliased_Talker применяет оператор as чтобы получить возможность использовать имплементацию bigTalk из B под дополнительным псевдонимом talk.

trait A <
public function smallTalk () <
echo ‘a’ ;
>
public function bigTalk () <
echo ‘A’ ;
>
>

trait B <
public function smallTalk () <
echo ‘b’ ;
>
public function bigTalk () <
echo ‘B’ ;
>
>

Изменение видимости метода

Используя синтаксис оператора as можно также настроить видимость метода в выставке класса.

Пример #6 Пример изменения видимости метода

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

Трейты, скомпонованные из трейтов

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

Пример #7 Пример трейтов, скомпонованных из трейтов

trait Hello <
public function sayHello () <
echo ‘Hello ‘ ;
>
>

trait World <
public function sayWorld () <
echo ‘World!’ ;
>
>

class MyHelloWorld <
use HelloWorld ;
>

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

Абстрактные члены трейтов

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

Пример #8 Экпресс требования с абстрактными методами

Статические члены трейта

Трейты могут определять и статические свойства и статические методы.

Пример #9 Статические переменные

class C1 <
use Counter ;
>

class C2 <
use Counter ;
>

Пример #10 Статические методы

trait StaticExample <
public static function doSomething () <
return ‘Что-либо делаем’ ;
>
>

class Example <
use StaticExample ;
>

Свойства

Трейты могут также определять свойства.

Пример #11 Определение свойств

class PropertiesExample <
use PropertiesTrait ;
>

Пример #12 Разрешение конфликтов

Источник

Трейты в PHP

PHP поддерживает только одиночное наследование: дочерний класс не может наследовать от нескольких классов сразу, только от одного единственного родителя. Однако в большинстве случаев было бы полезно наследовать от нескольких классов. Например, было бы желательно наследовать методы от нескольких разных классов, чтобы предотвратить дублирование кода. Трейты используются для восполнения этого пробела, позволяя нам повторно использовать одни и те же свойства и методы в нескольких классах.

Что такое трейты?

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

Синтаксис трейта такой же как и у класса, за исключением того, что имя трейта нужно объявлять с помощью ключевого слова trait :

Синтаксис

Экземпляр трейта, как и абстрактного класса, нельзя создать — трейты предназначены только для подключения к другим классам.

Синтаксис

Давайте посмотрим на пример в котором объявим один трейт и подключим к классу:

Пример

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

По сути, трейт — это просто способ скопировать и вставить код во время выполнения.

Использование нескольких трейтов

Пример

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

Чем трейты отличаются от интерфейсов?

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

Интерфейс — это контракт, в котором говорится, что «этот объект может делать это», тогда как трейт дает объекту возможность делать это.

Другими словами, если код ООП касается планирования и проектирования, то интерфейс — это план, а объект — полностью построенный дом. Между тем, трейты — это просто способ помочь построить дом, спроектированный по плану (интерфейсу).

Интерфейсы — это спецификации, которые можно проверить используя оператор instanceof (является ли текущий объект экземпляром указанного класса).

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

Пример

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

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

Переопределение унаследованных методов

Как описано в Руководстве: Унаследованный метод от базового класса переопределяется методом, вставленным с помощью трейта. Порядок приоритета таков — методы текущего класса переопределяют методы трейта, которые в свою очередь переопределяют унаследованные методы.

Итак, рассмотрим следующий сценарий:

Пример

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

При создании экземпляра MyClass, описанного выше, происходит следующее:

Заключение

Источник

Использование трейтов в PHP

using traits in php

Языки, подобные C++ и Python, позволяют нам наследоваться от нескольких классов, которые в какой-то мере решают эту проблему, а mixins в Ruby позволяет смешивать функциональность одного или нескольких классов без использования наследования. Но множественное наследование имеет свои проблемы.

Что представляет трейт

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

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

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

Трейт Singleton содержит реализацию шаблона Singleton со статическим методом getInstance(), который создает объект класса с использованием этого трейта (если он еще не создан) и возвращает его.

Попробуем создать объекты этих классов с помощью метода getInstance().

Мы можем видеть, что $a является объектом DbReader, а $b является объектом FileReader, но оба теперь ведут себя как объекты реализующие шаблон Singleton. Метод от класса Singleton был введен в классы, использующие его трейт.

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

Если мы просто расширим класс DbReader из родителя со скрытым свойством $instance, свойство не будет отображаться в дампе ReflectionClass::export().

Использование нескольких трейтов

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

Здесь у нас есть два трейта: «Привет» и «Мир». Трейт Hello может только сказать «Привет», а трейт World может сказать «Мир». В классе MyWorld мы применили Hello и World, чтобы объект MyWorld получал методы от обоих черт и мог сказать «Hello World».

В одной из следующих статей мы продолжим обсуждать трейты. А сегодня на этом все. Спасибо за внимание!

date article

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

Комментарии ( 0 ):

Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.

Copyright © 2010-2021 Русаков Михаил Юрьевич. Все права защищены.

Источник

Моя дача
Adblock
detector