文档章节

Yii框架 Event 和 Behavior理解

蔚蓝SG
 蔚蓝SG
发布于 2015/04/28 15:07
字数 1449
阅读 35
收藏 0

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();

 

 

© 著作权归作者所有

共有 人打赏支持
蔚蓝SG
粉丝 2
博文 2
码字总数 1805
作品 0
朝阳
高级程序员
私信 提问
yii2 - Property 之 默认构造方法 和 setter/getter 方法

yii2 内部规定了 construct 函数的构造形式,以键值对儿数组作为参数,进行属性的初始化,但要注意给属性赋值的工作是转交给基类 yiibaseYii::configure 方法的,故无法直接访问本类的私有属...

big_cat
2016/05/27
530
0
yii2 - Behavior 实例及源码分析

Behavior 的简述 行为简单来说是组件的扩展,可以对组件的属性,方法,事件 (yii2组件的三大要点)进行扩展而无需改动组件现有的代码逻辑。即此行为所拥有的属性,方法,事件,都会被绑定它...

big_cat
2016/06/01
811
0
Yii2 源码分析 - 入口文件执行流程

以 yii 2.0.14 高级版的 frontend 为例,从 frontend/web/index.php 开始 入口文件看着就这么几行,简单的很,那他是怎么通过这几行来运行应用的呢?先看 Yii.php 内的逻辑 接下来,就是重头...

botkenni
05/03
0
0
Ethan/yii2-serialized

Yii2 Serialized Attributes Behavior This Yii2 model behavior allows you to store arrays in attributes. To attach the behavior put the following code in your model: public functi......

Ethan
2016/03/28
0
0
yii2源码分析之执行基本流程

用yii2框架用了将近2年,一直都没有去看过它底层源码, 马上快不用了,最近对其源码研究一番,哈哈 废话少说,上代码, 入口文件是web/index.php

china_lx1
04/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

不学无数——SpringBoot入门IV

SpringBoot 1.Profiles Spring Profiles能够在不同的环境中使不同的应用配置生效。@Component和@Configuration两个注解都能够通过@Profiles来标记。下面是例子: @Configuration@Profile("b...

不学无数的程序员
10分钟前
1
0
nginx长连接出现504的解决办法

在http 中添加如下 fastcgi_connect_timeout 300s; fastcgi_send_timeout 300s; fastcgi_read_timeout 300s;...

hansonwong
11分钟前
0
0
记一次 Spring Boot多数据源 循环引用问题

如题,升级了一下mybatis版本后出现循环引用的问题。 具体异常如下 ***************************APPLICATION FAILED TO START***************************Description:The depen...

HeyS1
12分钟前
0
0
MongoDB Could not find host matching read preference { mode: \"primary\" } for set repl_shard1

最近在测试 MongoDB 4.0 分片集群 ,搭建好所有节点后,往mongos添加分片的时候,一直报错 Could not find host matching read preference { mode: \"primary\" } for set ,如下 mongos> sh...

xxj123gogo
16分钟前
0
0
linux安装java1.8

# tar -zxvf jdk-8u144-linux-x64.tar.gz vi /etc/profile export JAVA_HOME="/usr/local/java/jdk1.8.0_144" export CATALINA_HOME="/usr/local/tomcat/apache-tomcat-9.0.0.M22" export PA......

八戒八戒八戒
17分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部