yii浅析(三)

整个应用是基于下面这句代码运行起来的:

Yii::createWebApplication($env->configWeb)->run();

是的,就是这个run方法。我们在CWebApplication中找到run方法的实现:

public function run(){   
  //应用跑起来之前     
  if($this->hasEventHandler('onBeginRequest'))      
      $this->onBeginRequest(new CEvent($this));   
  //最后运行的方法   -- 1
  register_shutdown_function(array($this,'end'),0,false);   
  //处理请求
  $this->processRequest();   

  //处理请求结束之后 -- 2(2的运行顺序在1之前)
  if($this->hasEventHandler('onEndRequest'))      
      $this->onEndRequest(new CEvent($this));
}

接下来我们应该来说明这句代码:

$this->hasEventHandler('onBeginRequest')

不过在解释它之前,我们需要理解一下yii的event机制。

Yii的event机制

YII的事件机制,是其比较独特之处,合理使用好事件机制,会使各个组件之间的耦合更为松散,利于团体协作开发。

何时需要使用事件,如何给事件绑定事件处理函数,以及如何触发事件,与其它语言是有较大的差别的。例如Javascript中,可以使用

$(‘#id’).on("click",function() {});

方式给DOM元素绑定处理函数,当DOM元素上发生指定的事件(如click)时,将自动执行设定的函数。

但是PHP是服务器端的脚本语言,就不存在自动触发事件之说,所以和Javascript对比,YII中的事件是需要手动触发的。一般来说,要实现YII组件的事件机制,需要以下几步:

  1. 定义事件名称,其实就是级组件定义一个on开头的方法,其中的代码是固定的,如:
  public function onBeginRequest($event){
  $this->raiseEvent('onBeginRequest',$event);
}

即函数名与事件名是一致的。此步的作用就是将绑定在此事件上的处理函数逐个执行。写这一系列的播客,算是一个整理,所以我写细一点,现在把raiseEvent方法的代码贴出来。

/** * Raises an event. 
    * This method represents the happening of an event. It invokes 
    * all attached handlers for the event. 
    * @param string $name the event name 
    * @param CEvent $event the event parameter 
    * @throws CException if the event is undefined or an event handler is invalid. 
*/
    
    public function raiseEvent($name,$event){   
              $name=strtolower($name);   
              //_e这个数组用来存所有事件信息
              if(isset($this->_e[$name]))   {      
                    foreach($this->_e[$name] as $handler)  {         
                        if(is_string($handler)) 
                           call_user_func($handler,$event);             
                        elseif(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);  
                                     elseif(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;     
                   }  
               }   elseif(YII_DEBUG && !$this->hasEvent($name))      
                    throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',         array('{class}'=>get_class($this), '{event}'=>$name)));
  }

2 . 给组件对象绑定事件处理函数

$component->attachEventHandler($name, $handler);
$component->onBeginRequest = $handler ;

yii支持一个事件绑定多个回调函数,上述的两个方法都会在已有的事件上增加新的回调函数,而不会覆盖已有回调函数。

$handler即是一个PHP回调函数,关于回调函数的形式,本文的最后会附带说明。
如CLogRouter组件的init事件中,有以下代码:

Yii::app()->attachEventHandler('onEndRequest',array($this,'processLogs'));

这就是给CApplication对象的onEndRequest绑定了CLogRouter::processLogs()回调函数。而CApplication组件确实存在名为onEndRequest的方法(即onEndRequest事件),它之中的代码就是激活了相应的回调函数,即CLogRouter::processLogs()方法。所以从这里可以得出,日志的记录其实是发生在CApplication组件的正常退出时。

  1. 在需要触发事件的时候,直接激活组件的事件,即调用事件即可,如:
    比如CApplication组件的run方法中:
if($this->hasEventHandler('onBeginRequest'))
    $this->onBeginRequest(new CEvent($this));

这样即触发了事件处理函数。如果没有第一行的判断,那么在调试模式下(YII_DEBUG常量被定义为true),会抛出异常,而在非调试模式下(YII_DEBUG常量定义为false或没有定义YII_DEBUG常量),则不会产生任何异常。

回调函数的形式:

  1. 普通全局函数(内置的或用户自定义的)
call_user_func(‘print’, $str);
  1. 类的静态方法,使用数组形式传递
call_user_func(array(‘className’, ‘print’),  $str );
  1. 对象方法,使用数组形式传递
$obj = new className();
call_user_func(array($obj, ‘print’),  $str );
  1. 匿名方法,类似javascript的匿名函数
call_user_func(function($i){echo $i++;},4);

或使用以下形式:

$s = function($i) {
    echo $i++;
};
call_user_func($s,4);

总结: 关于Yii的事件机制其实就是提供了一种用于解耦的方式,在需要调用event的地方之前,只要你提供了事件的实现并注册在之后的地方需要的时候即可调用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,398评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,560评论 18 399
  • 朋友问我暗恋是什么感觉,下意识的回答:好像在商店看到喜欢的玩具,想买,钱不够,努力存钱,回头去看的时候发现涨价了,...
    WeiiiiiA阅读 92评论 0 0
  • 18号早上5点钟起床,天津去北京西路上,遇到北京地铁的早高峰,真的是挤之又挤,只想说北京的人真的太多了。北京西到福...
    Lqruc阅读 181评论 2 0