Chinaunix首页 | 论坛 | 博客
  • 博客访问: 910360
  • 博文数量: 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:54:09

个人觉得,在 Yii 里面,最难以明白的就是事件(Event)和行为(behavior)了。这不仅仅是因为它们的概念
比较难明,关键是它们的应用场景比较难明,不知道什么时候应该使用事件和行为来开发。
关于 Yii 的事件和行为的描述,可参考


事件
事件模型就是设计模式中的“观察者模式”:当对象的状态发生了变化,那么这个对象可以将该事件通知其它对象。
为了使用事件模型,需要实现这三个步骤:1、定义事件;2、注册事件句柄;3、触发事件。

 

为什么要做这三个步骤呢?因为对于 PHP 本身,它的执行过程不是以进程化来运行的,
所以 Yii 的事件触发机制不会像 ActionScript 3+ 那样,直接将触发事件。
有人说,Yii 的事件概念跟 js 中的事件概念差不多,因为 Yii 是将事件绑定到 Yii::app() 的执行过程中。
由于本人对于 js 的事件没有做过深入的了解,这里不敢贸然否定,或者肯定。

 

费话少说,先看这样的应用场景:
我想在请求过来的时候,先将请求的 IP 的记录到数据库,然后才进行对应的相应的请求处理。
1. 通过编辑 components/Controller.php 的构造方法来处理。
如代码:
  1. lass Controller extends CController  
  2. {  
  3.     public function __construct()  
  4.     {  
  5.         parent::__construct();  
  6.         //将请求的 IP 记录到数据库  
  7.     }  
  8. }  


2. 通过使用事件来处理。

我们来分析一个 framework/base/CApplication.php 的 run() 方法
  1. public function run()  
  2. {  
  3.     if($this->hasEventHandler('onBeginRequest'))  
  4.         $this->onBeginRequest(new CEvent($this));  
  5.     $this->processRequest();  
  6.     if($this->hasEventHandler('onEndRequest'))  
  7.         $this->onEndRequest(new CEvent($this));  
  8. }  

从代码可以看出来,在处理请求之前,Yii 首先会判断一下当前有没有处理 onBeginRequest 的函数或者类的方法绑定了,

如果有这样的函数或者类的方法存在,则先执行了它们,然后再处理请求。

 

那么,怎样写 onBeginRequest,或者怎样去调用呢?
方法一:修改 index.php
一般来说,我们的 index.php 最后一句是:
  1. Yii::createWebApplication($config)->run();  

我们在这里将它改造一下,改成:
  1. $app = Yii::createWebApplication($config);  
  2. Yii::app()->onBeginRequest=function($event) {  
  3.     //将请求的 IP 记录到数据库  
  4. };  
  5. Yii::app()->onBeginRequest=function($event) {  
  6.     //其它的你想要处理的内容,比如说,生成一个文件  
  7.     //file_put_contents('onBeginRequest.txt', '阿妈,我得左啦!');  
  8. };  
  9. $app->run();  


方法二:在配置文件 main.php 里面注册事件

如:
  1. /*************************************************** 
  2. 在我们想要的内容的前后出现了这些代码 
  3. 只是为了说明,我们添加的内容是要放在 
  4. 这个配置数据的一维里面。 
  5. 'import'=>array( 
  6.     'application.models.*', 
  7.     'application.components.*', 
  8.     'application.helpers.*', 
  9. ), 
  10. 'defaultController'=>'post', 
  11. ***************************************************/  
  12.   
  13. //其它代码  
  14. 'import'=>array(  
  15.     'application.models.*',  
  16.     'application.components.*',  
  17.     'application.helpers.*',  
  18. ),  
  19.   
  20. /************** 这才是我们想要添加的代码 **************/  
  21. 'onBeginRequest' => array('MyEventHandler''MyEventHandlerMethod'),  
  22.   
  23. 'defaultController'=>'post',  
  24. //其它代码  

关于 onBeginRequest 的使用,它必须是一个有效的 PHP 回调。

即,一个指匿名函数,全局函数名的字符串或一个数组。如果是数组,那么该数组包含两个元素,第一个元素是一个对象,第二个元素是这个对象的方法。
由此可见,方法一和方法二还是有点区别的。使用方法二的时候,只能注册一个 PHP 回调,而使用方法一,可以是不同的 PHP 回调。当然,这里说的方法二
只能注册一个 PHP 回调是指,对整个请求处理过程中肯定会执行的 PHP 回调,在其它地方需要的时候,也可以加上你想实现的功能。
 

3. 另一个例子,来说明自己是怎样定义一个事件的。

打开 models/ContactForm.php,输入
  1. /** 
  2.  * 自己定义发送邮件事件 
  3.  * @param unknown_type $event 
  4.  */  
  5. public function onSendMail($event)  
  6. {  
  7.     $this->raiseEvent('onSendMail',$event);  
  8. }  
  9.   
  10. /** 
  11.  * 验证成功,执行 
  12.  * @see CModel::afterValidate() 
  13.  */  
  14. public function afterValidate()   
  15. {  
  16.     if($this->hasEventHandler('onSendMail'))  
  17.         $this->onSendMail(new CEvent($this));  
  18. }  

这里我们定义了一个 onSendMail 事件,并在 Validate 验证后,触发此事件。

打开 controllers/SiteController.php,将修改actionContact修改为以下内容
  1. public function actionContact()  
  2. {  
  3.     $model=new ContactForm;  
  4.   
  5.     $model->onSendMail=function($event) {  
  6.         $headers="From: {$event->sender->email}\r\nReply-To: {$event->sender->email}";  
  7.         mail(Yii::app()->params['adminEmail'],$event->sender->subject,$event->sender->body,$headers);  
  8.     };  
  9.   
  10.     if(isset($_POST['ContactForm']))  
  11.     {  
  12.         $model->attributes=$_POST['ContactForm'];  
  13.         if($model->validate())  
  14.         {  
  15.   
  16.             Yii::app()->user->setFlash('contact','Thank you for contacting us. We will respond to you as soon as possible.');  
  17.             $this->refresh();  
  18.         }  
  19.     }  
  20.     $this->render('contact',array('model'=>$model));  
  21. }  

上面的 3 点,虽然通过绑定事件来做一些额外的处理,但同时已经暴露了一个问题,就是协同开发的时候,我不一定知道,

其他开发人员写了哪些事件的 PHP 回调,在处理过程中到底会调用哪些事件的 PHP 回调。或者说,这个 PHP 回调在什么时候创建的,
或者说你在为组件添加事件处理函数时,找不到合适的时候,如果添加早了,组件还没创建,如果添加晚了,事件不被执行,有可能组件已经执行完了。
我们需要一个类似于配置文件的东西,将存在的事件处理组织起来,统一管理。这个时候,行为可以用上了。

 

 

 

行为
这里先重新描述一下为什么要使用行为。
有两种办法可以对类添加特性:
1、直接修改这个类的代码,添加一些成员函数和成员变量;
2、派生,通过子类来扩展。
很明显第二种方法更加易维护、易扩展。但是如果需要对一个类添加多个特性(多人在不同时期),那么需要进行多级派生,这显然加大了维护成本。
在 Yii 里面,通过行为类绑定,组件将一个或多个 CBehavior 类的成员方法和成员变量添加到自己身上,并且在不需要的时候载掉某些 CBehavior 类。
同时,可以通过重写 CBehavior::events 的方法,来实现对目标类的多个事件绑定。这些事件将会在当前行为绑定到目标类的时候,一起被绑定上。

 

下面我们以代码来具体看一下这个行为特性。
在 protected 创建目录 behaviors,并在protected/behaviors目录下创建ApplicationBehavior.php,输入如下代码:

  1.     public function events()  
  2.     {  
  3.         return array_merge(parent::events(),array(  
  4.                 'onBeginRequest'=>'beginRequest'  
  5.         ));  
  6.     }  
  7.   
  8.     public function beginRequest($event)   
  9.     {  
  10.         echo "我已经将 onBeginRequest 的事件处理通过行为绑定了";  
  11.     }  
  12. }  

此行为文件,是要为 CApplication 服务,仔细查看这个行为文件,我们可以看到,events 方法定义了些行为可以处理的事件,

上面的类,可以处理 onBeginRequest 事件,当然如果你自己定义的组件也有一个叫做 onBeginRequest 方法,你也可以使用此行为
后面的 beginRequest 就是事件的处理函数,这个处理函数必须要有行为类中定义。

 

跟上面的事件一样,也有两种方法将此行为类附加到 CApplication。
方法一:
打开 index.php,输入下面代码
  1. $app = Yii::createWebApplication($config);  
  2. Yii::app()->onBeginRequest=function($event) {  
  3.     //将请求的 IP 记录到数据库  
  4. };  
  5. Yii::app()->onBeginRequest=function($event) {  
  6.     //file_put_contents('onBeginRequest.txt', '阿妈,我又得左啦!');  
  7. };  
  8. /****** 这句才是我们想要的东东 *********/  
  9. $app->attachBehavior('app''application.behaviors.ApplicationBehavior');  
  10. $app->run();  

刷新页面,你将会在头部看到一行 “我已经将 onBeginRequest 的事件处理通过行为绑定了”
 

方法二:

如果对 Yii 的组件定义了解的话,应该知道每一个组件,都有一个behaviors方法,该方法中定义的相关行为,在组件初始化时,会自动附件,
下面我们就为 CApplication 定义 behaviors,由于 CApplication 是系统级类,我们可以扩展此类,并添加behaviors方法。这里补充一下,
CApplication 是会根据 config/main.php 配置进行初始化,那么我们就可以将 behaviors 定义在 main.php。

 

打开 protected/config/main.php,加入如下代码:
  1. 'behaviors' => array(  
  2.     'app' => 'application.behaviors.ApplicationBehavior',  
  3. ),  

刷新页面,你也会在头部看到一行 “我已经将 onBeginRequest 的事件处理通过行为绑定了”

 

 

 

通过以上的例子,希望相关读者对 Yii 的事件和行为有一定的了解。
阅读(7263) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~