Yii框架 Event 和 Behavior理解

Event 

    Yii中的Event由两部分组成,分别是$events和EventHandler,其中$event代表事件发生时的具体数据,而EventHandler代表事件发生时的具体的处理函数。

    因此,当错误发生时,直接调用onError函数就可以,这时,之前被注册过的onError这个事件对应的错误处理函数都会被执行。具体的ErrorHandler发生过程可以通过raiseEvent()的源代码来了解。

    在Yii中,Event通常是在CComponent的子类中扩展出来的,一般以on开头,如:

    public function onError($event)

    {

            $this->raiseEvent('onError', $event);

    }

public function raiseEvent($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);  
             else if(is_callable($handler,true))  
             {  
                 if(is_array($handler))  
                 {  
                     // an array: 0 - object, 1 - method name  
                     list($object,$method)=$handler;  
                     if(is_string($object))    // static method call  
                         call_user_func($handler,$event);  
                     else if(method_exists($object,$method))  
                         $object->$method($event);  
                     else  
                         throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".',  
                             array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>$handler[1])));  
                 }  
                 else // PHP 5.3: anonymous function  
                     call_user_func($handler,$event);  
             }  
             else  
                 throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".',  
                     array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>gettype($handler))));  
             // stop further handling if param.handled is set true  
             if(($event instanceof CEvent) && $event->handled)  
                 return;  
         }  
     }  
     else if(YII_DEBUG && !$this->hasEvent($name))  
         throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',  
             array('{class}'=>get_class($this), '{event}'=>$name)));  
 }

    此函数的主要内容就是在CComponent的变量$_e寻找与事件名称(如本例中的'onError')对应的EventHandler(函数),然后调用该EventHandler,同时将事件发生时的数据作为参数传递给该函数,完成事件的处理。

    那么既然有EventHandler的调用,那么肯定就会有Event和EventHandler的注册了,否则就没有EventHandler可调用。

说到注册,自然会想到CComponent类中的__set()函数了,来看一下__set()函数的源码:

 public function __set($name,$value)  
         {  
                 $setter='set'.$name;  
                 if(method_exists($this,$setter))  
                         return $this->$setter($value);  
                 else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))  
                 {  
                         // duplicating getEventHandlers() here for performance  
                         $name=strtolower($name);  
                         if(!isset($this->_e[$name]))  
                                 $this->_e[$name]=new CList;  
                         return $this->_e[$name]->add($value);  
                 }  
                 else if(is_array($this->_m))  
                 {  
                         foreach($this->_m as $object)  
                         {  
                                 if($object->getEnabled() && (property_exists($object,$name) || $object->canSetProperty($name)))  
                                         return $object->$name=$value;  
                         }  
                 }  
                 if(method_exists($this,'get'.$name))  
                         throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',  
                                 array('{class}'=>get_class($this), '{property}'=>$name)));  
                 else  
                         throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',  
                                 array('{class}'=>get_class($this), '{property}'=>$name)));  
         }

    关于Event事件的注册是从第6行开始,从代码中可以看出Event名称必须以'on'开头,如onError,onClick等。对应的$value是一个List,即可以有多个EventHandler。

    具体的注册代码,Yii注释中也已经给出:

$component->onClick=$callback;    // or $component->onClick->add($callback);  

    总结一下,Event的原理就是首先要定义一个事件处理函数,其次将其注册到该类的对应的事件中去,最后在发生事件时调用该事件的EventHandler。实例如下,当改变类中的变量name时,打印字符串:

  class example extends CComponent
  {
        private $name;
        public function getName()
        {
            return isset($name) ? $name : '';
        }
        public function setName($newName)
        {
            $this->name = $newName;
            $this->raiseEvent('onChange', new CEvent());
        }
  }   
  //在某一controller中实现:
  $exa = new example();
  $exa->onChange = array($this, "showChange");
  $exa->setName("zhangsan");
  
  public function showChange()
  {
        echo "changed";
  }

Behavior

    Behavior是一种跟EventHandler功能比较类似的另一种表现形式,有点类似于多重继承,通过类绑定的方法,component将一个或者多个CBehavior类的成员方法和变量绑定到自己身上。

    通过调用CComponet中的attachBehavior方法实现关联,我们深入起源码看看究竟做了什么?

public function attachBehavior($name,$behavior)  //$name:行为名称 $behavior:行为对象
 {  
     if(!($behavior instanceof IBehavior))  
         $behavior=Yii::createComponent($behavior);  
     $behavior->setEnabled(true);  
     $behavior->attach($this);  
     return $this->_m[$name]=$behavior;  
 }

    程序很短,就是判断传入的behaviour是不是IBehavior的实例,根据情况做一下处理,反正最后是通过调用IBehavior的attach方法去挂到当前的CComponent方法。

    好了,关键的地方来了。注意看第七行,没有错,起把传入的behavior传入了 $this->_m[$name]变量,这个也就是为什么可以多绑定的原因,实现了收集的机制。转入IBehavior看attach方法定义

public function attach($owner)  
{  
    $this->_owner=$owner;  
    foreach($this->events() as $event=>$handler)  
        $owner->attachEventHandler($event,array($this,$handler));  
}

     分析这段代码,首先可以看出对于每一个Behavior,都只能属于一个Component.

       那么Event和Behavior又怎么会有关系了?看到 $owner->attachEventHandler($event,array($this,$handler));这句了吗?$owner就是我们绑定behavior的component,用该方法的名称就可以很容易的发现这里实现的Event Handler的绑定功能。

       这里的$this->events()是什么?

public function events()  
        {  
                return array();  
        }

         怎么突然和上面匹配不了了?返回了一个空的array()?千万别搞错,这个函数是一个virtual的函数是需要重写的。你其实这里就可以理解为events()方法返回的是一个含有事件名称和事件处理方法的关联数组。

        如果还不能理解,我们看下CModelBehavior的这段的具体实现,他可是继承自CBehavior的。

public function events()  
{  
    return array(  
        'onAfterConstruct'=>'afterConstruct',  
        'onBeforeValidate'=>'beforeValidate',  
        'onAfterValidate'=>'afterValidate',  
    );  
}

    看到了吗?定义了三个事件,以及相应的事件处理函数,只是这些事件处理函数,在你扩展的时候,需要重写。

    总结一下,behavior其实与event很类似,只不过behavior是将事件分类整理到一起,归到一个类中,然后一起注册给component,因此behavior可以看做是一个事件与事件处理函数的集合。需要注意的是,当一个behavior被attach到一个component时,该behavior的所有函数都能被component调用。具体事例如下:

    首先重写Behavior类:

    class exampleBehavior extends CBehavior
    {
            //重写关联数组
            public function events()
            {
                    return array(
                            'onBegin' => 'begin';
                            'onEnd' => 'end';
                            'onClick' => 'click';
                    );
            }
            //事件处理函数
            public function begin()
            {
            }
            public function end()
            {
            }
            public function click()
            {
            }
    }
   //在某一个controller中实现:
    $exBehavior = new exampleBehavior();
    $component->attachBehavior('example', 'application.behavior.exampleBehavior');
    $component->begin();

 

 

转载于:https://my.oschina.net/u/2338047/blog/407234

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值