加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > PHP > 正文

深入解析PHP的Yii框架中的event事件机制

发布时间:2020-05-27 23:53:34 所属栏目:PHP 来源:互联网
导读:这篇文章主要介绍了PHP的Yii框架中的event事件机制,文中讲解了Yii的事件处理器以及给组件对象绑定事件处理函数等重要知识,需要的朋友可以参考下

事件

事件可以将自定义代码“注入”到现有代码中的特定执行点。附加自定义代码到某个事件,当这个事件被触发时,这些代码就会自动执行。例如,邮件程序对象成功发出消息时可触发 messageSent 事件。如想追踪成功发送的消息,可以附加相应追踪代码到messageSent 事件。 Yii 引入了名为 yiibaseComponent 的基类以支持事件。如果一个类需要触发事件就应该继承 yiibaseComponent 或其子类。

Yii的event机制

YII的事件机制,是其比较独特之处,合理使用好事件机制,会使各个组件之间的耦合更为松散,利于团体协作开发。 何时需要使用事件,如何给事件绑定事件处理函数,以及如何触发事件,与其它语言是有较大的差别的。例如Javascript中,可以使用

方式给DOM元素绑定处理函数,当DOM元素上发生指定的事件(如click)时,将自动执行设定的函数。 但是PHP是服务器端的脚本语言,就不存在自动触发事件之说,所以和Javascript对比,YII中的事件是需要手动触发的。一般来说,要实现YII组件的事件机制,需要以下几步:

定义事件名称,其实就是级组件定义一个on开头的方法,其中的代码是固定的,如:

raiseEvent('onBeginRequest',$event); }

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

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','{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.','{event}'=>$name)));
}

事件处理器(Event Handlers)

事件处理器是一个PHP 回调函数,当它所附加到的事件被触发时它就会执行。可以使用以下回调函数之一:

  • 字符串形式指定的 PHP 全局函数,如 'trim' ;
  • 对象名和方法名数组形式指定的对象方法,如 [$object,$method] ;
  • 类名和方法名数组形式指定的静态类方法,如 [$class,$method] ;
  • 匿名函数,如 function ($event) { ... } 。

事件处理器的格式是:

通过 $event 参数,事件处理器就获得了以下有关事件的信息:

  • yiibaseEvent::name:事件名
  • yiibaseEvent::sender:调用 trigger() 方法的对象
  • yiibaseEvent::data:附加事件处理器时传入的数据,默认为空,后文详述

附加事件处理器

调用 yiibaseComponent::on() 方法来附加处理器到事件上。如:

// 处理器是全局函数
$foo->on(Foo::EVENT_HELLO,'function_name');

// 处理器是对象方法
$foo->on(Foo::EVENT_HELLO,[$object,'methodName']);

// 处理器是静态类方法
$foo->on(Foo::EVENT_HELLO,['appcomponentsBar','methodName']);

// 处理器是匿名函数
$foo->on(Foo::EVENT_HELLO,function ($event) {
//事件处理逻辑
});
附加事件处理器时可以提供额外数据作为 yiibaseComponent::on() 方法的第三个参数。数据在事件被触发和处理器被调用时能被处理器使用。如:

// 当事件被触发时以下代码显示 "abc"
// 因为 $event->data 包括被传递到 "on" 方法的数据
$foo->on(Foo::EVENT_HELLO,function ($event) {
echo $event->data;
},'abc');

事件处理器顺序

可以附加一个或多个处理器到一个事件。当事件被触发,已附加的处理器将按附加次序依次调用。如果某个处理器需要停止其后的处理器调用,可以设置 $event 参数的 [yiibaseEvent::handled]] 属性为真,如下:

on(Foo::EVENT_HELLO,function ($event) { $event->handled = true; });

默认新附加的事件处理器排在已存在处理器队列的最后。因此,这个处理器将在事件被触发时最后一个调用。在处理器队列最前面插入新处理器将使该处理器最先调用,可以传递第四个参数 $append 为假并调用 yiibaseComponent::on() 方法实现:

on(Foo::EVENT_HELLO,function ($event) { // 这个处理器将被插入到处理器队列的第一位... },$data,false);

触发事件

事件通过调用 yiibaseComponent::trigger() 方法触发,此方法须传递事件名,还可以传递一个事件对象,用来传递参数到事件处理器。如:

use yiibaseComponent;
use yiibaseEvent;

class Foo extends Component
{
const EVENT_HELLO = 'hello';

public function bar()
{
$this->trigger(self::EVENT_HELLO);
}
}

以上代码当调用 bar() ,它将触发名为 hello 的事件。

提示:推荐使用类常量来表示事件名。上例中,常量 EVENT_HELLO 用来表示 hello 。这有两个好处。第一,它可以防止拼写错误并支持 IDE 的自动完成。第二,只要简单检查常量声明就能了解一个类支持哪些事件。 有时想要在触发事件时同时传递一些额外信息到事件处理器。例如,邮件程序要传递消息信息到 messageSent 事件的处理器以便处理器了解哪些消息被发送了。为此,可以提供一个事件对象作为 yiibaseComponent::trigger() 方法的第二个参数。这个事件对象必须是 yiibaseEvent 类或其子类的实例。如:

use yiibaseComponent;
use yiibaseEvent;

class MessageEvent extends Event
{
public $message;
}

class Mailer extends Component
{
const EVENT_MESSAGE_SENT = 'messageSent';

public function send($message)
{
// ...发送 $message 的逻辑...

$event = new MessageEvent;
$event->message = $message;
$this->trigger(self::EVENT_MESSAGE_SENT,$event);

}
}

当 yiibaseComponent::trigger() 方法被调用时,它将调用所有附加到命名事件(trigger 方法第一个参数)的事件处理器。

移除事件处理器

从事件移除处理器,调用 yiibaseComponent::off() 方法。如:

off(Foo::EVENT_HELLO,'function_name');

// 处理器是对象方法
$foo->off(Foo::EVENT_HELLO,'methodName']);

// 处理器是静态类方法
$foo->off(Foo::EVENT_HELLO,'methodName']);

// 处理器是匿名函数
$foo->off(Foo::EVENT_HELLO,$anonymousFunction);

注意当匿名函数附加到事件后一般不要尝试移除匿名函数,除非你在某处存储了它。以上示例中,假设匿名函数存储为变量$anonymousFunction 。

移除事件的全部处理器,简单调用 yiibaseComponent::off() 即可,不需要第二个参数:

off(Foo::EVENT_HELLO);

类级别的事件处理器

以上部分,我们叙述了在实例级别如何附加处理器到事件。有时想要一个类的所有实例而不是一个指定的实例都响应一个被触发的事件,并不是一个个附加事件处理器到每个实例,而是通过调用静态方法 yiibaseEvent::on() 在类级别附加处理器。

例如,活动记录对象要在每次往数据库新增一条新记录时触发一个 yiidbBaseActiveRecord::EVENT_AFTER_INSERT 事件。要追踪每个活动记录对象的新增记录完成情况,应如下写代码:

Event::on(ActiveRecord::className(),ActiveRecord::EVENT_AFTER_INSERT,function ($event) {
Yii::trace(get_class($event->sender) . ' is inserted');
});

每当 yiidbBaseActiveRecord 或其子类的实例触发 yiidbBaseActiveRecord::EVENT_AFTER_INSERT 事件时,这个事件处理器都会执行。在这个处理器中,可以通过 $event->sender 获取触发事件的对象。

当对象触发事件时,它首先调用实例级别的处理器,然后才会调用类级别处理器。

可调用静态方法yiibaseEvent::trigger()来触发一个类级别事件。类级别事件不与特定对象相关联。因此,它只会引起类级别事件处理器的调用。如:

Event::on(Foo::className(),Foo::EVENT_HELLO,function ($event) {
echo $event->sender; // 显示 "appmodelsFoo"
});

Event::trigger(Foo::className(),Foo::EVENT_HELLO);

注意这种情况下 $event->sender 指向触发事件的类名而不是对象实例。

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读