Yii框架组件的事件机制原理与用法分析
本文实例讲述了Yii框架组件的事件机制原理与用法。分享给大家供大家参考,具体如下:
在深入分析Yii的运行之前,我们先来看一下Yii框架中一个很重要的机制-事件。
Yii官方参考文档关于组件事件的解释:
=======================================================================
组件事件是一些特殊的属性,它们使用一些称作事件句柄(eventhandlers)的方法作为其值。附加(分配)一个方法到一个事件将会引起方法在事件被唤起处自动被调用。因此,一个组件的行为可能会被一种在部件开发过程中不可预见的方式修改。
组件事件以on开头的命名方式定义。和属性通过getter/setter方法来定义的命名方式一样,事件的名称是大小写不敏感的。以下代码定义了一个onClicked事件:
publicfunctiononClicked($event)
{
$this->raiseEvent('onClicked',$event);
}
这里作为事件参数的$event是CEvent或其子类的实例。
我们可以附加一个方法到此event,如下所示:
$component->onClicked=$callback;
这里的$callback指向了一个有效的PHP回调。它可以是一个全局函数也可以是类中的一个方法。如果是后者,它必须以一个数组的方式提供:array($object,'methodName').
事件句柄的结构如下:
functionmethodName($event)
{
......
}
这里的$event即描述事件的参数(它来源于raiseEvent()调用)。$event参数是CEvent或其子类的实例。至少,它包含了关于谁触发了此事件的信息。
从版本1.0.10开始,事件句柄也可以是一个PHP5.3以后支持的匿名函数。例如,
$component->onClicked=function($event){
......
}
如果我们现在调用onClicked(),onClicked事件将被触发(在onClicked()中),附属的事件句柄将被自动调用。
一个事件可以绑定多个句柄。当事件触发时,这些句柄将被按照它们绑定到事件时的顺序依次执行。如果句柄决定组织后续句柄被执行,它可以设置$event->handled为true。
=======================================================================
从这一句开始”我们可以附加一个方法到此event“,读者可能就不知道是什么意思了,于是看一下CComponent的源码:
/**
*Raisesanevent.
*Thismethodrepresentsthehappeningofanevent.Itinvokes
*allattachedhandlersfortheevent.
*@paramstringtheeventname
*@paramCEventtheeventparameter
*@throwsCExceptioniftheeventisundefinedoraneventhandlerisinvalid.
*/
publicfunctionraiseEvent($name,$event)
{
//事件名称同一小写化处理
$name=strtolower($name);
//先查看成员变量是否有以此命名的事件
if(isset($this->_e[$name]))
{
//如果有,这个成员保存的是每一个事件处理器
//以数组的方式保存
foreach($this->_e[$name]as$handler)
{
//如果事件处理器是一个字符串,那么就是一个全局函数
if(is_string($handler))
call_user_func($handler,$event);
//如果不是,那么有可能是一个数组,该数组包含一个对象和方法名
//参考http://php.net/manual/en/function.is-callable.php
elseif(is_callable($handler,true))
{
//anarray:0-object,1-methodname
list($object,$method)=$handler;
//如果对象是一个对象名
if(is_string($object))//staticmethodcall
call_user_func($handler,$event);
//判断对象是否有要调用的方法
elseif(method_exists($object,$method))
$object->$method($event);
else
thrownewCException(Yii::t('yii','Event"{class}.{event}"isattachedwithaninvalidhandler
"{handler}".',
array('{class}'=>get_class($this),'{event}'=>$name,'{handler}'=>$handler[1])));
}
else
thrownewCException(Yii::t('yii','Event"{class}.{event}"isattachedwithaninvalidhandler
"{handler}".',
array('{class}'=>get_class($this),'{event}'=>$name,'{handler}'=>gettype($handler))));
//stopfurtherhandlingifparam.handledissettrue
//如果想停止继续循环获取事件的handler
//那么需要设置event的handled为true
if(($eventinstanceofCEvent)&&$event->handled)
return;
}
}
elseif(YII_DEBUG&&!$this->hasEvent($name))
thrownewCException(Yii::t('yii','Event"{class}.{event}"isnotdefined.',
array('{class}'=>get_class($this),'{event}'=>$name)));
//如果_e中没有这个成员也没关系
}
我们再看一下CEvent的代码(CComponent.php):
classCEventextendsCComponent
{
/**
*@varobjectthesenderofthisevent
*/
public$sender;
/**
*@varbooleanwhethertheeventishandled.Defaultstofalse.
*Whenahandlersetsthistrue,therestuninvokedhandlerswillnotbeinvokedanymore.
*/
public$handled=false;
/**
*Constructor.
*@parammixedsenderoftheevent
*/
publicfunction__construct($sender=null)
{
$this->sender=$sender;
}
}
CEvent只包含两个变量$sender记录事件触发者,$handled表示事件是否已经被“解决”。
接着我们再看一下如何给一个组件注册一个事件处理器:
/** *Attachesaneventhandlertoanevent. * *AneventhandlermustbeavalidPHPcallback,i.e.,astringreferringto *aglobalfunctionname,oranarraycontainingtwoelementswith *thefirstelementbeinganobjectandthesecondelementamethodname *oftheobject. * *Aneventhandlermustbedefinedwiththefollowingsignature, **functionhandlerName($event){} **where$eventincludesparametersassociatedwiththeevent. * *Thisisaconvenientmethodofattachingahandlertoanevent. *Itisequivalenttothefollowingcode: **$component->getEventHandlers($eventName)->add($eventHandler); ** *Using{@linkgetEventHandlers},onecanalsospecifytheexcutionorder *ofmultiplehandlersattachingtothesameevent.Forexample: **$component->getEventHandlers($eventName)->insertAt(0,$eventHandler); **makesthehandlertobeinvokedfirst. * *@paramstringtheeventname *@paramcallbacktheeventhandler *@throwsCExceptioniftheeventisnotdefined *@seedetachEventHandler */ publicfunctionattachEventHandler($name,$handler) { $this->getEventHandlers($name)->add($handler); } /** *Returnsthelistofattachedeventhandlersforanevent. *@paramstringtheeventname *@returnCListlistofattachedeventhandlersfortheevent *@throwsCExceptioniftheeventisnotdefined */ publicfunctiongetEventHandlers($name) { if($this->hasEvent($name)) { $name=strtolower($name); if(!isset($this->_e[$name])) //新建一个CList保存事件的处理器 $this->_e[$name]=newCList; return$this->_e[$name]; } else thrownewCException(Yii::t('yii','Event"{class}.{event}"isnotdefined.', array('{class}'=>get_class($this),'{event}'=>$name))); }
由此可以看出,首先获取事件处理器对象,如果没有则使用CList(Yii实现的一个链表)创建,然后将事件处理器add进这个对象中,这样就可以在raiseEvent时遍历所有的事件处理器进行处理了,有点儿类似jQuery中注册了多个click事件处理器之后,当click事件触发时,会按顺序调用之前注册的事件处理器。
更多关于Yii相关内容感兴趣的读者可查看本站专题:《Yii框架入门及常用技巧总结》、《php优秀开发框架总结》、《smarty模板入门基础教程》、《php面向对象程序设计入门教程》、《php字符串(string)用法总结》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》
希望本文所述对大家基于Yii框架的PHP程序设计有所帮助。