Что такое trait php

Примеси в PHP (trait) // PHP

Что такое trait php. Смотреть фото Что такое trait php. Смотреть картинку Что такое trait php. Картинка про Что такое trait php. Фото Что такое trait php

С версии 5.4 в PHP появился такой интересный механизм как примеси (trait), который по задумке разработчиков должен помочь разруливать ситуации когда уж очень хочется применить множественное наследование, но нельзя. Вот о некоторых подобных ситуациях я и расскажу далее.

Примеры не надуманные, а вполне рабочие из фреймворка Bluz 😉

Теория

Тут будет краткий пересказ официальной документации в моей интерпретации, если не интересно — промотайте чуть дальше…

Хотя нет, лучше останьтесь и прочитайте, ведь мне абсолютно не нравится подача этого материала в официальной документации, для понимания примесей надо разговор начинать с необходимости данного нововведения в PHP. Начну издалека, вот один из классических примеров ООП: есть следующие классы – абстрактный класс Мебель, который лишь знает что у мебели есть размеры, Стол с площадью столешницы и Стул с некой максимально-возможной нагрузкой, в довесок есть Диван, пока просто диван:

Вот такая у нас простая мебель получается, теперь давайте размышлять как мы можем расширить данные классы, и каким образом мы бы это делали:

Давайте-ка распишем данный подход в коде:

Да тут невооруженным взглядом видно копипасту, и очень хотелось бы избавится от неё, хотелось бы реализацию требований 3 и 4 закинуть в отдельный класс, и наследовать его, но в PHP нет множественного наследования, может быть только один класс предок. И вот в PHP 5.4 на сцену выходят примеси (trait), чем-то они схожи на классы, но лишь издалека, примеси лишь группируют некий набор функционала под одной вывеской, но не более. Давайте таки опишем необходимый функционал в примесях:

Теперь данный примеси легко можно подключить в наших классах:

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

Реализация шаблона Singleton

Можно много спорить о данном шаблоне, есть у него и плюсы и минусы, но речь не об этом, а о его реализации, при чём так, в один use 😉

Чтобы это стало возможным следует реализовать вот такую примесь:

Полный листинг класса можно найти в репозитории – Bluz/Common/Singleton.php. Пример не претендует на универсальность, но он юзабелен и имеет право на жизнь.

Обратите внимание, реализация шаблона Singleton никоим образом не обязует вас на хоть какие-то объединения классов под одним предком, т.к. это выглядило бы очень странно, а вот для избавления от практики копирования функционала из класса в класс нам и нужен данный trait

Реализация интерфейса инициализации

Теперь попробуем заюзать данную примесь в простом шаблонизаторе:

А вот и пример использования:

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

Реализация помощников класса

О чём это я, да о достаточно популярном приёме, когда функционал одного класса разделяют по различным классам и функциям с ленивой инициализацией, самый наглядный пример — это помощники View в Zend Framework. В фреймворке Bluz данный подход реализован в одном trait:

Для примера будем использовать всё тот же шаблонизатор:

В качестве ленивого помощника у нас будет анонимная функция:

Теперь можно пользоваться:

Код сокращён и упрощён для наглядности

Выводы

Как можно заметить, trait’ы можно и нужно использовать, ведь таким образом мы сокращаем объём кода, который нам потребуется поддерживать, да и метод копи-пасты уже давно должен был кануть в лету, а с появлением примесей вам уж не будет оправдания 🙂

Если у вас есть примеры использования примесей, прошу — оставляйте ссылки.

Источник

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

Что такое trait php. Смотреть фото Что такое trait php. Смотреть картинку Что такое trait php. Картинка про Что такое trait php. Фото Что такое trait 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».

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

Что такое trait php. Смотреть фото Что такое trait php. Смотреть картинку Что такое trait php. Картинка про Что такое trait php. Фото Что такое trait php

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

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

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

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

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

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

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

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

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

Источник

Что такое trait php. Смотреть фото Что такое trait php. Смотреть картинку Что такое trait php. Картинка про Что такое trait php. Фото Что такое trait phpТрейты в PHP

Нужные для использования в классе трейты можно указать через запятую:

Приоритет методов при работе с трейтами.

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

Конфликты трейтов.

Ошибки могут быть когда подключается несколько трейтов, содержащие одни и те же методы. Или когда класс наследует у другого класса с подключением трейта, который уже был подключен в родительском классе.
Для разрешения конфликтов необходимо использовать оператор insteadof при подключении трейтов для того, чтобы точно выбрать один из конфликтных методов.
Внутри тела «use» мы использовали ключевое слово insteadof, слева от которого указывается трейт, метод которого будем использовать и имя самого метода, которые разделяются двойным двоеточием. В правой части указывается имя трейта, метод которого должен быть заменён.

Если же второй одноименный метод (из другого трейта) нам тоже нужен, то можно применить псевдоним имени используя ключевое слово as :

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

Используются так же как и в классах:

Доступ к свойствам базового класса.

В трейтах для доступа к свойствам базового класса можно использовать псевдопеременную $this.

Изменения прав доступа к методам трейта.

Внутри трейта мы можем использовать любой модификатор доступа (public, private, protected) для методов. Но, кроме этого, есть возможность в классе менять этот модификатор на другой. Для этого в теле use после слова as можно указать новый модификатор.

Источник

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 Разрешение конфликтов

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *