Поведение и события

Оригинал: Behaviors & events

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

События

Эта функция полезна в том случае, если вы хотите прервать нормальное функционирование приложения без расширения базовых классов.
Например, включение gzip сжатия на выходе может быть сделано через расширение CWebApplication. Но поскольку для обработчиков Событий предусмотрены точки входа, можно сделать следующее:
Yii::app()->onBeginRequest = create_function('$event', 'return ob_start("ob_gzhandler");');
Yii::app()->onEndRequest = create_function('$event', 'return ob_end_flush();');

Вы можете создать обработчик Событий (это просто метод в некоторых классах с определенной подписью) и прикрепить его к Событию объекта. Вы можете добавить к объектам сколько угодно обработчиков Событий.
Если обработчик Событий является статическим, то вы можете создать объект необходимого назначения:
$test_comp->onSomethingGoesOn = array(new SomeClass, 'eventHandler1');
$test_comp->onSomethingGoesOn = array(new SomeOtherClass, 'eventHandler2');
$test_comp->onSomethingGoesOn = array(new YetAnotherClass, 'eventHandler3');

До тех пор пока вы взаимодействуете с объектом, вы можете добавить обработчик Событий к нему.
Если понадобится, вы можете вызвать Событие вроде этого:
$test_comp->onSomethingGoesOn(new CEvent($this));
$test_comp->onSomethingGoesOn(new CEvent());

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

Поведение

Поведение – это просто способ добавления методов к объекту.
Возьмем такой сценарий:
У вас есть 2 класса: MySuperClass1 и MySuperClass2. В обоих классах имеется множество методов. У вас может возникнуть необходимость подключить их к новому классу, скажем к MyBoringClass. Но, к сожалению, средствами языка PHP это сделать невозможно:
class MyBoringClass extends MySuperClass1, MySuperClass2 {
}

Здесь и на сцену выходит Поведение. Вот что получается:
class MyBoringClass extends MySuperClass1 {
}
 
$classInstance = new MyBoringClass();
$classInstance->attachbehavior('uniqueName', new MySuperClass2);

В результате $classInstance обладает всеми методами MySuperClass1 и MySuperClass2. Изначально MySuperClass2 используется как Поведение, которое расширяет CBehavior. Единственное ограничение состоит в том, что прикрепленное Поведение не может переопределять методы класса компонента, которому оно присвоено. Если метод уже существует или если он добавился от оригинального класса или от ранее подключенного Поведения, то он не будет перезаписан.
В объектно-ориентированном языке, таком как, например, Ruby, вполне возможно начать с совершенно пустого объекта и, постепенно двигаясь вперед, выстраивать его Поведение. В Yii-фреймворке Поведение реализовано с некоторыми особенностями. Смысл в том, что класс, к которому вы хотите добавить Поведение, должен расширять Cbehavior.
class SomeClass extends CBehavior
{
    public function add($x, $y) { return $x + $y; }
}

Тогда используйте:
$test_comp = new TestComponent(); 
$test_comp->attachbehavior('blah', new SomeClass);
$test_comp->add(2, 5);

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

После изучения данного материала можно перечитать соответствующие разделы руководства, поскольку там содержится много полезного (например, вы можете найти достаточно информации по интерфейсам, в частности, как реализовать IBehavior до расширения CBehavior).

4 комментария

avatar
Про поведения в руководстве написно мало. Про события вполне достаточно. Советую изучить подробные описания здесь и здесь.
avatar
*написано
avatar
Если использовать новые версии php, то в поведениях yii практически нет смыла. Они ни чем не отличаются от трейтов. Разве что поведение можно отключить. Но у трейтов есть существенный плюс — ide нормально подсвечивает методы из трейтов.

А где Вы используете трейты? Интересен опыт других разработчиков.
avatar
При использовании лишь для добавления методов — так в этом не отличаются. А в остальном…

Трейты нельзя настроить, у них нет своих личных переменных и private методов, их нельзя подключить по два раза к одной модели, они не могут навесить себя на события, например, на beforeSave или выполнить какой-либо код (например, добавить владельцу валидатор) во время прикрепления. Конечно же, это всё можно эмулировать, добавив переменные в модель и дёргая вручную методы трейтов из beforeSave модели. То есть используя методы трейта, фактически, как методы обычного статического хелпера.

Например, в посте блога для обработки двух полей я осуществляю фильтрацию их одним и тем же поведением, плюс поведение для загрузки изображений само добавляет в модель file-валидатор для нужного поля в своём методе attach():

return array(
    'PurifyShort'=>array(
	'class'=>'DPurifyTextBehavior',
	'sourceAttribute'=>'short',
	'destinationAttribute'=>'short_purified',
	'purifierOptions'=> array(
	    'HTML.Nofollow' => true,
	),
    ),
    'PurifyText'=>array(
	'class'=>'DPurifyTextBehavior',
	'sourceAttribute'=>'text',
	'destinationAttribute'=>'text_purified',	
	'enableMarkdown'=>true,
	'purifierOptions'=> array(
	    'Attr.AllowedRel'=>array('nofollow'),
	    'HTML.SafeObject'=>true,
	    'Output.FlashCompat'=>true,
	),
    ),
    'ImageUpload'=>array(
	'class'=>'DFileUploadBehavior',
	'fileAttribute'=>'image',
    ),
);


Каждый экземпляр живёт своей жизнью, то есть хранит свои настройки в приватных полях и независимо от остальных что-то делает в beforeSave. На чистых трейтах реализовать такое мало шансов.

IDE тоже легко подсвечивает методы из поведений, если их вписать самому в PHPDoc директиве @method класса.

Оставить комментарий