Статьи

Под капотом компонентной архитектуры Yii, часть 2

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

События

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

Как правило, есть три шага для реализации события:

  1. Определить событие
  2. Прикрепить пользовательскую функциональность к событию
  3. Запустить событие, когда это произойдет

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

 <?php public function onUserRegistered($event) { $this->raiseEvent("onUserRegistered", $event); } 

События в Yii определяются простым добавлением функции с префиксом «on» к имени метода. Чтобы прикрепить функцию к объекту, вам необходимо иметь доступ к объекту из других областей приложения. Событие добавляется в пользовательский компонент, чтобы к нему можно было обращаться через приложение Yii::app()->user .

Запускающие события

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

 <?php public function actionUserRegister() { // code to register a user and add to the database // ... // user has been successfully registered so lets raise an event to tell the world $e = new CEvent($this, array("user" => $user)); Yii::app()->user->onUserRegistered($e); } 

Как только пользователь будет успешно зарегистрирован и добавлен в вашу базу данных, вы захотите рассказать миру. Этот код вызывает событие. События не знают конкретных деталей вызываемой ими функции, поэтому все функции обработки должны иметь одинаковый интерфейс. В Yii все функции обработки ожидают один параметр, который является объектом CEvent . Объект CEvent ожидает два параметра: ссылку на объект, CEvent событие, и необязательный массив параметров, которые можно использовать для хранения конкретной информации, относящейся к конкретному событию (эти параметры могут быть доступны позже вашим функциям обработки). В этом примере я хочу, чтобы все функции обработки имели доступ к объекту пользователя, который был только что зарегистрирован. Для этого я передаю массив array("user" => $user) где $user — это объект, представляющий нашего недавно зарегистрированного пользователя. Затем я запускаю событие и raiseEvent() функцию raiseEvent() , вызывая функцию onUserRegistered() и onUserRegistered() объект события.

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

Прикрепление событий

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

 <?php public function init() { Yii::app()->user->onUserRegistered = array($this, "sendMyEmail"); } public function sendMyEmail($event) { $user = $event->params["user"]; mail($user->email, "Welcome to this amazing event app", "Hello..."); } 

Я прикрепил метод sendMyEmail() к событию onUserRegistered поэтому теперь, когда новый пользователь регистрируется, будет вызываться функция sendMyEmail() . Кроме того, в PHP 5.3 или выше вы можете указать анонимную функцию. Yii поддерживает присвоение любого значения событию, которое также возвращает true, если передано в is_callable() PHP is_callable() .

Магия йии

Теперь давайте посмотрим, как Yii реализует события; все события управляются CComponent классе CComponent . Чтобы реализовать события, класс компонента должен реализовать три основных понятия:

  • Определение событий — Yii нужно хранить или иметь возможность поиска определенных событий.
  • Запуск событий — когда происходит событие, Yii необходимо вызвать все наши функции PHP, связанные с этим событием.
  • Присоединение событий — Yii нужен механизм для хранения списка допустимых обратных вызовов PHP для события.

Механизм определения событий в Yii состоит в том, чтобы просто создать функцию с префиксом «on», как вы видели ранее. Механизм для запуска события заключается в добавлении $this->raiseEvent("onMethodName"); внутри определенного события и просто вызовите метод, когда событие происходит в вашем коде. Это оставляет нам две детали реализации:

  • Прикрепление функций к событию.
  • Вызов всех функций, прикрепленных к событию, когда оно запускается.

Чтобы прикрепить событие, вы используете код onMyEventName = callback , что означает, что реализация для присоединения функций к событиям должна обрабатываться в методе magic __set компонентов.

 <?php public function __set($name, $value){ if (strncasecmp($name, "on", 2) === 0 && method_exists($this, $name)) { $name = strtolower($name); if (!isset($this->_e[$name])) { $this->_e[$name] = new CList(); } return $this->_e[$name]->add($value); } } 

Реализация сначала проверяет, начинается ли значение $name с текста «on» и существует ли метод с тем же именем, что и значение. Если это так, Yii предполагает, что $value является представлением обратного вызова, который необходимо прикрепить к событию, определенному $name . У Yii есть закрытая переменная-член $_e которая содержит массив обратных вызовов, заданных именами событий, и просто добавляет обратный вызов в список для конкретного ключа события.

  $ _e => массив (
     'onUserRegistered' => массив (
         0 => массив (объект, 'sendMyEmail')
     ),
     'OnSomeOtherEvent' => массив (
         0 => функция () {}
         1 => ...
     )
 ) 

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

 public function raiseEvent($name, $event) { foreach ($this->_e[$name] as $handler) { if (is_array($handler)) { // an array: 0 - object, 1 - method name list($object, $method) = $handler; $object->$method($event); } else { // PHP 5.3+ anonymous function call_user_func($handler, $event); } } } 

Я реорганизовал метод raiseEvent() чтобы его было проще читать для нашей демонстрации, но реализация Yii имеет больше проверки на ошибки и обрабатывает дополнительные типы обратного вызова, такие как статические методы.

Резюме

События — отличная концепция для реализации надежного, гибкого кода. В этой статье вы узнали, как создавать повторно используемые компоненты, функциональность которых может быть расширена за счет использования событий, и увидели, как класс CComponent платформы Yii реализует и управляет событиями в PHP. Конечно, детали использования и реализации могут быть специфическими для Yii, но сама концепция универсальна.

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

Изображение через Филипчука Олега Васильевича / Shutterstock