Событие компонентов в php

PSR-14 — главное событие в PHP

В прошлом году PHP-FIG — Группа концепций совместимости PHP, выпустила несколько новых спецификаций. Последняя из них — PSR-14, посвящена диспетчеризации событий. Как и другие PSR, это локальная спецификация, но имеет большое влияние на многие аспекты стандартизации.

От переводчика: Это перевод первой части целой серии публикаций, в которой Larry (Crell) Garfield, один из членов PHP-FIG, объясняет, что такое PSR-14, на что способен, а на что нет, и как лучше всего использовать его в своих проектах.

Цель

Диспетчеризация событий давно используется во многих языках. Если вы когда-нибудь использовали EventDispatcher в Symfony, Event system в Laravel, хуки в Drupal, Event Manager во фреймворке Zend, пакет League\Event, или что-то подобное, то понимаете о чём речь.

В общем смысле, все эти системы представляют из себя некую форму «наблюдателя-посредника». Один фрагмент кода отправляет сообщение типа — «событие», а посредник передает его другим отдельным фрагментам кода — «слушателям». Иногда сигнал направлен только в одну сторону, иногда «слушатель» может как-то передавать данные обратно вызывающей стороне. Конечно же, они все разные и не очень совместимы между собой.

Это проблема для автономных библиотек, которые хотят подключаться к различным библиотекам и приложениям. Многие библиотеки можно расширить через отправку событий в той или иной форме, чтобы другой код мог связаться с ними. Но такой посреднический слой, фактически, проприетарный. Библиотека, которая запускает Symfony EventDispatcher, затем объединяется с Symfony. Тогда использование ее где-то еще требует установки EventDispatcher и соединения с библиотеками в программе. Библиотека, которая вызывает связывающую систему от Drupal module_invoke_all() , затем связывается с Drupal. И так далее.

Читайте также:  Passing array html form

Цель PSR-14 — избавить библиотеки от этой зависимости. Это позволяет библиотекам расширяться через тонкий общий слой, и потом облегчит их перенос в другую среду без дополнительных усилий и затрат, например, в Symfony, Zend Framework, Laravel, TYPO3, eZ Platform или Slim. Пока у среды есть совместимость с PSR-14, всё будет работать.

Спецификация

Как уже говорил, спецификация довольно легкая. Это три интерфейса в одном методе и мета-описание, как их использовать. Все просто и удобно. Ниже код этих интерфейсов (без комментариев для экономии места).

namespace Psr\EventDispatcher; interface EventDispatcherInterface < public function dispatch(object $event); >interface ListenerProviderInterface < public function getListenersForEvent(object $event) : iterable; >interface StoppableEventInterface

Первые два это ядро спецификации. StoppableEventInterface — это расширение, к которому вернёмся позже.

Думаю, EventDispatcher большинству из вас знаком — это всего лишь объект с методом, которому вы передаете событие — посредник, о котором уже говорили. Само событие, однако, не определено — им может быть любой PHP-объект. Подробнее об этом позже.

Большинство существующих реализаций имеют один объект или набор функций, которые работают как посредник или диспетчер и место для регистрации кода, которое получает событие (слушателей). Для PSR-14 мы сознательно разделили эти две обязанности на отдельные объекты. Диспетчер получает список слушателей от объекта поставщика, который составляет этот список.

Откуда тогда поставщик получает список слушателей? Да откуда хочет! Существует миллиард и один способ связать слушателя и событие, все они абсолютно действующие и несовместимые. Еще в начале мы решили, что стандартизация «Единого Истинного Пути» регистрации слушателей будет слишком ограничена. Однако, стандартизировав процесс подключения слушателя к диспетчеру, можно получить отличную гибкость, не заставляя пользователя делать что-то странное и непонятное.

Также в коде не указывается, что представляют из себя слушатель. Им может быть любой способный к восприятию сигнала фрагмент PHP: функция, анонимная функция, метод объекта, всё что угодно. Поскольку вызываемый объект может делать что угодно, это значит, что допустимо иметь в качестве слушателя, скажем, анонимную функцию, которая выполняет отложенную загрузку сервиса из DI-контейнера и вызывает в сервисе метод, который на самом деле и содержит слушающий код.

Вкратце, диспетчер это простой и лёгкий API для авторов библиотек. Поставщики слушателей предлагают надёжный и гибкий API для интеграторов фрэймворков, а отношения между диспетчером и провайдером объединяют их вместе.

Простой пример

В общем виде, схема объединения всех частей в целое, будет выглядеть примерно так.

class Dispatcher implements EventDispatcherInterface < public function __construct(ListenerProviderInterface $provider) < $this->provider = $provider; > public function dispatch(object $event) < foreach ($this->provider->getListenersForEvent($event) as $listener) < $listener($event); >return $event; > > $dispatcher = new Dispatcher($provider); $event = new SomethingHappened(); $dispatcher->dispatch($event); 

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

Код

PSR-14 уже поддерживается основными фреймворками и приложениями.

  • Matthew Weier O’Phinney уже обязался ввести поддержку PSR-14 в zend-eventmanager 4.0 во фрэймворке Zend.
  • Symfony недавно анонсировали изменения в EventDispatcher для совместимости с PSR-14, что даст полную поддержку в 5.0/5.1.
  • Фрэймворк Yii объявил о намерении интегрировать PSR-14 в версии 3.0.
  • Benni Mack из TYPO3 CMS заявил, что в следующем релизе TYPO3 все существующие концепции типа «ловушка+сигнал/слот» будут поддерживать PSR-14.
  • Tukio от Larry Garfield, автора этой статьи.
  • Phly Event Dispatcher от Matthew Weier O’Phinney.
  • Kart от Benni Mack, который работает как встраиваемый плагин.

Узнать больше подробностей можно или из оригинала следующей части и документации или 17 мая на PHP Russia. Второй вариант привлекателен по нескольким причинам. Например, глава Программного комитета Александр (samdark) Макаров в числе тех, кто внедрил PSR-14 в Yii. Да и в принципе состав Программного комитета и спикеров невероятно силен, вряд ли найдется хоть одна тема из сферы профессионального использования PHP, которую не удастся обсудить на этой конференции.

Источник

Компонент

Yii-приложения состоят из компонентов–объектов, созданных согласно спецификациям. Компонент (component) — это экземпляр класса CComponent или производного от него. Использование компонента в основном включает доступ к его свойствам, а также вызов и обработку его событий. Базовый класс CComponent устанавливает то, как определяются свойства и события.

1. Свойство компонента ¶

Свойство компонента схоже с публичной переменной-членом класса (public member variable). Мы можем читать или устанавливать его значение. Например:

$width=$component->textWidth; // получаем значение свойства textWidth $component->enableCaching=true; // устанавливаем значение свойства enableCaching

Для того, чтобы создать свойство компонента необходимо просто объявить публичную переменную в классе компонента. Более гибкий вариант — определить методы, считывающие (getter) и записывающие (setter) это свойство, например:

public function getTextWidth() < return $this->_textWidth; > public function setTextWidth($value) < $this->_textWidth=$value; >

В приведенном коде определено свойство textWidth (имя нечувствительно к регистру), доступное для записи.

При чтении вызывается метод чтения getTextWidth() , возвращающий значение свойства. Соответственно, при записи будет вызван метод записи setTextWidth() . Если метод записи не определен, свойство будет доступно только для чтения, а при попытке записи будет вызвано исключение. Использование методов чтения и записи имеет дополнительное преимущество: при чтении или записи значения свойства могут быть выполнены дополнительные действия (такие как проверка на корректность, вызов события и др.).

Примечание: Есть небольшая разница в определении свойства через методы и через простое объявление переменной. В первом случае имя свойства нечувствительно к регистру, во втором — чувствительно.

2. События компонента ¶

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

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

public function onClicked($event) < $this->raiseEvent('onClicked', $event); >

где $event — это экземпляр класса CEvent или производного от него, представляющего параметр события. К событию можно подключить обработчик, как показано ниже:

$component->onClicked=$callback;

где $callback — это корректный callback-вызов PHP (см. PHP-функцию call_user_func). Это может быть либо глобальная функция, либо метод класса. В последнем случае вызову должен передаваться массив: array($object,’methodName’) .

Обработчик события должен быть определен следующим образом:

где $event — это параметр, описывающий событие (происходит из вызова raiseEvent() ). Параметр $event — это экземпляр класса CEvent или его производного. Как минимум, он содержит информацию о том, кто вызвал событие.

Если теперь вызвать onClicked() , событие onClicked будет вызвано (внутри onClicked() ), и прикрепленный обработчик события будет запущен автоматически.

К событию может быть прикреплено несколько обработчиков. При возникновении события обработчики будут вызваны в том порядке, в котором они были прикреплены к событию. Если в обработчике необходимо предотвратить вызов последующих обработчиков, необходимо установить $event->handled в true .

3. Поведение компонента ¶

Начиная с версии 1.0.2, к компоненту была добавлена поддержка примесей (mixin) и теперь к компоненту можно прикрепить одно или несколько поведений. Поведение — это объект, чьи методы могут быть «унаследованы» компонентом, к которому прикреплены, посредством объединения функционала вместо четкой специализации (как в случае обычного наследования класса). К компоненту можно прикрепить несколько поведений и таким образом получить «множественное наследование».

Поведение классов должно реализовывать интерфейс IBehavior. Большинство поведений могут быть созданы путем расширения базового класса CBehavior. В случае, если поведение необходимо прикрепить к модели, его можно создать на основе класса CModelBehavior или класса CActiveRecordBehavior, который реализует дополнительные, специфические для модели возможности.

Чтобы воспользоваться поведением, его необходимо прикрепить к компоненту путем вызова метода поведения attach(). Далее мы вызываем метод поведения через компонент:

// $name уникально идентифицирует поведения в компоненте $component->attachBehavior($name,$behavior); // test() является методом $behavior $component->test();

К прикрепленному поведению можно обращаться, как к обычному свойству компонента. Например, если поведение с именем tree прикреплено к компоненту, мы можем получить ссылку на этот объект поведения следующим образом:

$behavior=$component->tree; // эквивалентно выражению: // $behavior=$component->asa('tree');

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

$component->disableBehavior($name); // выражение ниже приведет к вызову исключения $component->test(); $component->enableBehavior($name); // здесь все будет работать нормально $component->test();

В случае, когда два поведения, прикрепленные к одному компоненту, имеют методы с одинаковыми именами, преимущество будет иметь метод поведения, которое было прикреплено раньше.

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

Источник

Оцените статью