Chinaunix首页 | 论坛 | 博客
  • 博客访问: 909120
  • 博文数量: 91
  • 博客积分: 803
  • 博客等级: 准尉
  • 技术积分: 1051
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-24 13:42
文章分类

全部博文(91)

文章存档

2021年(1)

2020年(4)

2019年(4)

2018年(9)

2017年(11)

2016年(11)

2015年(6)

2014年(3)

2013年(28)

2012年(14)

分类: 系统运维

2012-09-05 15:58:23

Event                                                                                                                                                 

Event其实是个既复杂又简单的问题,因为里面总是有很多概念容易交织,在理解Event前,我们首先要明白Event的概念。

Event中往往有两个角色,一个是Event,一个是Event Handler。为什么需要两个?

原因很容易理解,Event只是说明这件事情发生了,但是没有说怎么处理。这部分处理的工作当然是留给了Event Handler了。

我在这里主要从和现实世界的相似度上说,不涉及设计模式上的讲解。有意向的朋友可以自己去参看相关的模式,资料还是很多的。

言归正传,对于Event和Event Handler的概念一定要时刻烂熟于胸,否则,很容易混淆的。

在这里我们主要针对Yii中的情况进行分析

对于Event在Yii中的理解,首先要清楚这么几件事情

1、  在Yii中Event一般是在CComponet的子类中扩展出来的,一般以on开头。

  1. public function onClick($event)  
  2.  {  
  3.      $this->raiseEvent('onClick',$event);  
  4.  }  

上面是一段例子,用来定义Event的。但是你要注意这里的$event可不是我们前面说的Event,而应该说$event和这个onClick函数整体构成了一个Event

其实这也很容易理解,因为一个Event的构成需要一个标志说明Event发生,还需要一些数据来说明这个Event发生时的情况。

2、  如raiseEvent的机制要明白,就是要看源码。

我们首先看CComponent的raiseEvent的源码,当然避免不了的还有CComponet的源码

在讲解raiseEvent前,我们需要看CComponent中的一段代码,是__get函数的

  1. public function __get($name)  
  2.          {  
  3.                  $getter='get'.$name;  
  4.                  if(method_exists($this,$getter))  
  5.                          return $this->$getter();  
  6.                  else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))  
  7.                  {  
  8.                          // duplicating getEventHandlers() here for performance  
  9.                          $name=strtolower($name);  
  10.                          if(!isset($this->_e[$name]))  
  11.                                  $this->_e[$name]=new CList;  
  12.                          return $this->_e[$name];  
  13.                  }  
  14.                  else if(isset($this->_m[$name]))  
  15.                          return $this->_m[$name];  
  16.                  else if(is_array($this->_m))  
  17.                  {  
  18.                          foreach($this->_m as $object)  
  19.                          {  
  20.                                  if($object->getEnabled() && (property_exists($object,$name) || $object->canGetProperty($name)))  
  21.                                          return $object->$name;  
  22.                          }  
  23.                  }  
  24.                  throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',  
  25.                          array('{class}'=>get_class($this), '{property}'=>$name)));  
  26.          }  

这里其实完成的功能很简单,就是针对不同的情况来完成对$this->property的调用操作,这个应该属于php的了,就不多说了。

我们在这里主要需要注意的是$this->_e[$name]这个变量,在程序的第6-13行,这段是针对事件做的操作。

这里,只要清楚,这个变量是一个CLIST类型,并且是和所设置的具体的Event的相关的即可。

接下来,我们可以看具体的raiseEvent代码了,如下:

  1. public function raiseEvent($name,$event)  
  2.  {  
  3.      $name=strtolower($name);  
  4.      if(isset($this->_e[$name]))  
  5.      {  
  6.          foreach($this->_e[$nameas $handler)  
  7.          {  
  8.              if(is_string($handler))  
  9.                  call_user_func($handler,$event);  
  10.              else if(is_callable($handler,true))  
  11.              {  
  12.                  if(is_array($handler))  
  13.                  {  
  14.                      // an array: 0 - object, 1 - method name  
  15.                      list($object,$method)=$handler;  
  16.                      if(is_string($object))    // static method call  
  17.                          call_user_func($handler,$event);  
  18.                      else if(method_exists($object,$method))  
  19.                          $object->$method($event);  
  20.                      else  
  21.                          throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".',  
  22.                              array('{class}'=>get_class($this), '{event}'=>$name'{handler}'=>$handler[1])));  
  23.                  }  
  24.                  else // PHP 5.3: anonymous function  
  25.                      call_user_func($handler,$event);  
  26.              }  
  27.              else  
  28.                  throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".',  
  29.                      array('{class}'=>get_class($this), '{event}'=>$name'{handler}'=>gettype($handler))));  
  30.              // stop further handling if param.handled is set true  
  31.              if(($event instanceof CEvent) && $event->handled)  
  32.                  return;  
  33.          }  
  34.      }  
  35.      else if(YII_DEBUG && !$this->hasEvent($name))  
  36.          throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',  
  37.              array('{class}'=>get_class($this), '{event}'=>$name)));  
  38.  }  

第四行,我们发现了$this->_e[$name]变量,从第四行往下看,我们不难发现,原来这个变量是一个存放Event Handler的地方,就是放具体要怎么处理这个事件的函数。

这里需要插播一段,你可能会奇怪,没有见到具体的Event Handler传入啊。我引用了一段CComponent说明的程序来说明,

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

看到这里,很自然的就要想到找CComponet中的__set函数

  1. public function __set($name,$value)  
  2.          {  
  3.                  $setter='set'.$name;  
  4.                  if(method_exists($this,$setter))  
  5.                          return $this->$setter($value);  
  6.                  else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))  
  7.                  {  
  8.                          // duplicating getEventHandlers() here for performance  
  9.                          $name=strtolower($name);  
  10.                          if(!isset($this->_e[$name]))  
  11.                                  $this->_e[$name]=new CList;  
  12.                          return $this->_e[$name]->add($value);  
  13.                  }  
  14.                  else if(is_array($this->_m))  
  15.                  {  
  16.                          foreach($this->_m as $object)  
  17.                          {  
  18.                                  if($object->getEnabled() && (property_exists($object,$name) || $object->canSetProperty($name)))  
  19.                                          return $object->$name=$value;  
  20.                          }  
  21.                  }  
  22.                  if(method_exists($this,'get'.$name))  
  23.                          throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',  
  24.                                  array('{class}'=>get_class($this), '{property}'=>$name)));  
  25.                  else  
  26.                          throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',  
  27.                                  array('{class}'=>get_class($this), '{property}'=>$name)));  
  28.          }  

从第六行看起,发现了嘛?又是Event的相关部分,看到第12行,发现了吗?把$callback存入了$this->_e[$name]变量。好,说道这里,我们又要再回到raiseEvent中。。。

其实接下来的代码也很容易明白,就是运行这个Event Handler了。里面一些判断也只是针对了不同形式的$Callback而做的。

貌似。。。Event部分讲完了。。。没有错其实就是这么简单的事情。。

总结一下,就是先在Componet的相关子类 中1、定义Event,就是onXXX的函数。(以上只是定义的时候做的,接下来的可以在定义中定义,也可以根据情况动态处理。)2、需要再赋予相应的 EventHandler就是callback3、调用相应的事件,就是在你觉得会出现这个事件地方加入这个事件函数,即可。比如在一个Click函数的 开头放入onClick,表示在执行Click动作之前。

Behaviour                                                                                                                                                     

Behaviour中文名为行为,那么他到底是什么呢?和Event又是什么关系呢?

其实在Yii中这个Behaviour是Event Handler的另一种表现形式而已,和我们之前说的$callback其实是一回事,只是为了扩展和使用,做了进一步的封装。

在具体解释Behaviour之前,我们要理解一个概念Mixin。。。这个可不是"迷信“的意思。。我觉得应该是Mix in的意思,但是不确定,这是个什么东西呢,他其实什么都不是,就是一种概念。

这个概念具体指什么?就是指一个类提供了某种功能可以被其子类继承或复用,而不需要实例化。对于一般的语言都只可以单继承,而mixin的表现更类似于种 多继承。他强调,从mixin继承的不是一种特性,而是收集功能的手段。很神奇吧?也很晦涩吧。其实也就是算法的事情。我们到具体的实例中就容易理解了。

接下里我们看看Yii中的实现,首先先看behaviour的使用方法。

  1. // $name uniquely identifies the behavior in the component  
  2. $component->attachBehavior($name,$behavior);  
  3. // test() is a method of $behavior  
  4. $component->test();  

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

  1. public function attachBehavior($name,$behavior)  
  2.  {  
  3.      if(!($behavior instanceof IBehavior))  
  4.          $behavior=Yii::createComponent($behavior);  
  5.      $behavior->setEnabled(true);  
  6.      $behavior->attach($this);  
  7.      return $this->_m[$name]=$behavior;  
  8.  }  

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

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

你应该注意到,我这里遗漏了一个关键的地方,就是第六行!这个部分才是Mixin的核心所在。让我们转到IBehavior先看方法定义。

  1. /** 
  2.   * IBehavior interfaces is implemented by all behavior classes. 
  3.   * 
  4.   * A behavior is a way to enhance a component with additional methods that 
  5.   * are defined in the behavior class and not available in the component class. 
  6.   * 
  7.   * @author Qiang Xue  
  8.   * @version $Id$ 
  9.   * @package system.base 
  10.   * @since 1.0.2 
  11.   */  
  12.  interface IBehavior  
  13.  {  
  14.          /** 
  15.           * Attaches the behavior object to the component. 
  16.           * @param CComponent $component the component that this behavior is to be attached to. 
  17.  */  
  18.          public function attach($component);  
  19.          /** 
  20.           * Detaches the behavior object from the component. 
  21.           * @param CComponent $component the component that this behavior is to be detached from. 
  22.  */  
  23.          public function detach($component);  
  24.          /** 
  25.           * @return boolean whether this behavior is enabled 
  26.  */  
  27.          public function getEnabled();  
  28.          /** 
  29.           * @param boolean $value whether this behavior is enabled 
  30.  */  
  31.          public function setEnabled($value);  
  32.  }  

主要看attach方法,他是一个behavior绑定到component的一个方法,具体的实现在其子类,我们继续看下去,看CBehavior

其中对于attach的实现,如下

  1. public function attach($owner)  
  2. {  
  3.     $this->_owner=$owner;  
  4.     foreach($this->events() as $event=>$handler)  
  5.         $owner->attachEventHandler($event,array($this,$handler));  
  6. }  

详细分析这段代码,这里完成了,设置behavior的绑定对象,这也就是说,一个behavior对象只可以为一个component服务,所谓的收集功能主要是前面所说的 $this->_m[$name]起的作用。

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

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

  1. public function events()  
  2.         {  
  3.                 return array();  
  4.         }  

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

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

  1. public function events()  
  2. {  
  3.     return array(  
  4.         'onAfterConstruct'=>'afterConstruct',  
  5.         'onBeforeValidate'=>'beforeValidate',  
  6.         'onAfterValidate'=>'afterValidate',  
  7.     );  
  8. }  


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

言归正传,继续回到Behavior的attach方法中的这句$owner->attachEventHandler($event,array($this,$handler));语句。

我们跟进去看看这个Componet中的attachEventHandler方法到底搞了写什么。

  1. public function attachEventHandler($name,$handler)  
  2.          {  
  3.                  $this->getEventHandlers($name)->add($handler);  
  4.          }  
  5.    
  6.          public function getEventHandlers($name)  
  7.          {  
  8.                  if($this->hasEvent($name))  
  9.                  {  
  10.                          $name=strtolower($name);  
  11.                          if(!isset($this->_e[$name]))  
  12.                                  $this->_e[$name]=new CList;  
  13.                          return $this->_e[$name];  
  14.                  }  
  15.                  else  
  16.                          throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',  
  17.                                  array('{class}'=>get_class($this), '{event}'=>$name)));  
  18.          }  

attachEventHandler方法中调用了getEventHandlers方法,该方法就是返回$this->_e[$name]变量,然后$handler存入,注意这里的$handler其实对应调用处的array($this,$handler)。

结合前面Event中说的,一切都很显然了。

总结一下,behavior其实可以认为是一种将事件分类整理在一起,归成一类,然后,一起注册进去,这样更方便维护和管理,同时也更面向对象。所以behavior也可以认为是一种事件和其处理方法的集合。


Ok,绕来绕去终于绕出来了。其实整个还是很简单的。但是这种idea还是很好的,让Behavior让事件也可以复用,或者可以说让事件的类型可以方便的归类。


作者:AnyKoro
    
阅读(6434) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~