热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Yii分析2:组件的事件机制

在深入分析Yii的运行之前,我们先来看一下Yii框架中一个很重要的机制–事件

在深入分析 Yii 的运行之前,我们先来看一下 Yii 框架中一个很重要的机制 – 事件。

 

Yii 官方参考文档关于组件事件的解释:

 

=======================================================================

组件事件是一些特殊的属性,它们使用一些称作 事件句柄 ( event handlers )的方法作为其值。 附加 ( 分配 ) 一个方法到一个事件将会引起方法在事件被唤起处自动被调用。因此, 一个组件的行为可能会被一种在部件开发过程中不可预见的方式修改。

组件事件以 on 开头的命名方式定义。和属性通过 getter/setter 方法来定义的命名方式一样, 事件的名称是大小写不敏感的。以下代码定义了一个 onClicked 事件 :

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


这里作为事件参数的 $event 是 CEvent 或其子类的实例。

我们可以附加一个方法到此 event ,如下所示 :

PHP
$component->OnClicked=$callback;
1
    $component->OnClicked=$callback;

 

这里的 $callback 指向了一个有效的 PHP 回调。它可以是一个全局函数也可以是类中的一个方法。 如果是后者,它必须以一个数组的方式提供 : array($object,’methodName’).

事件句柄的结构如下:

PHP
function methodName($event) { ...... }
1
2
3
4
    function methodName($event)  
    {  
        ......  
    }

 

这里的 $event 即描述事件的参数(它来源于 raiseEvent() 调用)。 $event 参数是 CEvent 或其子类的实例。 至少,它包含了关于谁触发了此事件的信息。

从版本 1.0.10 开始,事件句柄也可以是一个 PHP 5.3 以后支持的匿名函数。例如,

PHP
$component->OnClicked=function($event) { ...... }
1
2
3
    $component->OnClicked=function($event) {  
        ......  
    }

 

如果我们现在调用 onClicked() , onClicked 事件将被触发(在 onClicked() 中), 附属的事件句柄将被自动调用。

一个事件可以绑定多个句柄。当事件触发时, 这些句柄将被按照它们绑定到事件时的顺序依次执行。如果句柄决定组织后续句柄被执行,它可以设置 $event->handled 为 true 。

 

=======================================================================

 

从这一句开始”我们可以附加一个方法到此 event “,读者可能 就不知道是什么意思了,于是看一下 CComponent 的源码:

PHP
/** * Raises an event. * This method represents the happening of an event. It invokes * all attached handlers for the event. * @param string the event name * @param CEvent the event parameter * @throws CException if the event is undefined or an event handler is invalid. */ 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); //如果不是,那么有可能是一个数组,该数组包含一个对象和方法名 //参考http://php.net/manual/en/function.is-callable.php else if(is_callable($handler,true)) { // 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 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 //如果想停止继续循环获取事件的handler //那么需要设置event的handled为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))); //如果_e中没有这个成员也没关系 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    /**
         * Raises an event.
         * This method represents the happening of an event. It invokes
         * all attached handlers for the event.
         * @param string the event name
         * @param CEvent the event parameter
         * @throws CException if the event is undefined or an event handler is invalid.
         */  
        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);  
                    //如果不是,那么有可能是一个数组,该数组包含一个对象和方法名  
                    //参考http://php.net/manual/en/function.is-callable.php  
                    else if(is_callable($handler,true))  
                    {  
                        // 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  
                        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  
                    //如果想停止继续循环获取事件的handler  
    //那么需要设置event的handled为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)));  
            //如果_e中没有这个成员也没关系  
        }

我们再看一下 CEvent 的代码( CComponent.php ):

PHP
class CEvent extends CComponent { /** * @var object the sender of this event */ public $sender; /** * @var boolean whether the event is handled. Defaults to false. * When a handler sets this true, the rest uninvoked handlers will not be invoked anymore. */ public $handled=false; /** * Constructor. * @param mixed sender of the event */ public function __construct($sender=null) { $this->sender=$sender; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    class CEvent extends CComponent  
    {  
        /**
         * @var object the sender of this event
         */  
        public $sender;  
        /**
         * @var boolean whether the event is handled. Defaults to false.
         * When a handler sets this true, the rest uninvoked handlers will not be invoked anymore.
         */  
        public $handled=false;  
 
        /**
         * Constructor.
         * @param mixed sender of the event
         */  
        public function __construct($sender=null)  
        {  
            $this->sender=$sender;  
        }  
    }

 

CEvent 只包含两个变量 $sender 记录事件触发者, $handled 表示事件是否已经被“解决”。

接着我们再看一下如何给一个组件注册一个事件处理器:

PHP
/** * Attaches an event handler to an event. * * An event handler must be a valid PHP callback, i.e., a string referring to * a global function name, or an array containing two elements with * the first element being an object and the second element a method name * of the object. * * An event handler must be defined with the following signature, *
 * function handlerName($event) {} * 
* where $event includes parameters associated with the event. * * This is a convenient method of attaching a handler to an event. * It is equivalent to the following code: *
 * $component->getEventHandlers($eventName)->add($eventHandler); * 
* * Using {@link getEventHandlers}, one can also specify the excution order * of multiple handlers attaching to the same event. For example: *
 * $component->getEventHandlers($eventName)->insertAt(0,$eventHandler); * 
* makes the handler to be invoked first. * * @param string the event name * @param callback the event handler * @throws CException if the event is not defined * @see detachEventHandler */ public function attachEventHandler($name,$handler) { $this->getEventHandlers($name)->add($handler); } /** * Returns the list of attached event handlers for an event. * @param string the event name * @return CList list of attached event handlers for the event * @throws CException if the event is not defined */ public function getEventHandlers($name) { if($this->hasEvent($name)) { $name=strtolower($name); if(!isset($this->_e[$name])) //新建一个CList保存事件的处理器 $this->_e[$name]=new CList; return $this->_e[$name]; } else throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.', array('{class}'=>get_class($this), '{event}'=>$name))); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    /**
         * Attaches an event handler to an event.
         *
         * An event handler must be a valid PHP callback, i.e., a string referring to
         * a global function name, or an array containing two elements with
         * the first element being an object and the second element a method name
         * of the object.
         *
         * An event handler must be defined with the following signature,
         *
							
         * function handlerName($event) {}
         *
         * where $event includes parameters associated with the event.
         *
         * This is a convenient method of attaching a handler to an event.
         * It is equivalent to the following code:
         *
							
         * $component->getEventHandlers($eventName)->add($eventHandler);
         *
         *
         * Using {@link getEventHandlers}, one can also specify the excution order
         * of multiple handlers attaching to the same event. For example:
         *
							
         * $component->getEventHandlers($eventName)->insertAt(0,$eventHandler);
         *
         * makes the handler to be invoked first.
         *
         * @param string the event name
         * @param callback the event handler
         * @throws CException if the event is not defined
         * @see detachEventHandler
         */  
        public function attachEventHandler($name,$handler)  
        {  
            $this->getEventHandlers($name)->add($handler);  
        }  
        /**
         * Returns the list of attached event handlers for an event.
         * @param string the event name
         * @return CList list of attached event handlers for the event
         * @throws CException if the event is not defined
         */  
        public function getEventHandlers($name)  
        {  
            if($this->hasEvent($name))  
            {  
                $name=strtolower($name);  
                if(!isset($this->_e[$name]))  
                    //新建一个CList保存事件的处理器  
                    $this->_e[$name]=new CList;  
                return $this->_e[$name];  
            }  
            else  
                throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',  
                    array('{class}'=>get_class($this), '{event}'=>$name)));  
    }

由此可以看出,首先获取事件处理器对象,如果没有则使用 CList ( Yii 实现的一个链表)创建,然后将事件处理器 add 进这个对象中,这样就可以在 raiseEvent 时遍历所有的事件处理器进行处理了,有点儿类似 jQuery 中注册了多个 click 事件处理器之后,当 click 事件触发时,会按顺序调用之前注册的事件处理器。


推荐阅读
  • 本文探讨了如何在JavaScript中调用PHP函数及实现两者之间的有效交互,包括通过AJAX请求、动态生成JavaScript代码等方法。 ... [详细]
  • 解析程序员与软件工程师的角色差异
    本文深入探讨了程序员与软件工程师之间的主要区别,包括它们的职业定位、技能要求以及工作内容等方面的不同,旨在帮助读者更好地理解这两个角色的特点。 ... [详细]
  • 本文详细探讨了 Java 中 Daemon 线程的特点及其应用场景,并深入分析了 Random 类的源代码,帮助开发者更好地理解和使用这些核心组件。 ... [详细]
  • ThinkPHP 文件缓存组件详解与应用
    本文深入探讨了ThinkPHP框架中的文件缓存类实现,提供了详细的代码示例和使用说明,旨在帮助开发者更好地理解和利用这一功能来优化应用程序性能。 ... [详细]
  • ASP.NET Core Razor模型验证绑定是否造成服务器资源浪费?
    探讨了ASP.NET Core Razor页面中模型验证绑定机制是否导致了不必要的服务器负载。通过一个简单的登录表单示例,分析了模型验证的过程及其对服务器的影响。 ... [详细]
  • 本文探讨了如何利用 Application 对象在 PHP 应用程序中共享数据,特别是在多用户环境中保持数据的一致性和安全性。文章还介绍了 Application 对象的基本结构、方法和事件,并提供了实际应用示例。 ... [详细]
  • 解决MySQL Administrator 登录失败问题
    本文提供了解决在使用MySQL Administrator时遇到的登录错误的方法,包括启动变量和服务部分禁用的问题。同时,文章还介绍了通过安全配置模式来解决问题的具体步骤。 ... [详细]
  • django项目中使用手机号登录
    本文使用聚合数据的短信接口,需要先获取到申请接口的appkey和模板id项目目录下创建ubtils文件夹,定义返回随机验证码和调取短信接口的函数function.py文件se ... [详细]
  • CSGO
    CSGOTimeLimit:40002000MS(JavaOthers)MemoryLimit:524288524288K(JavaOthers)ProblemDescriptio ... [详细]
  • 本文介绍了如何通过自定义View中的declare-styleable属性创建枚举类型,并在代码中访问这些枚举值的方法。 ... [详细]
  • 本文探讨了Windows Presentation Foundation (WPF)如何通过扩展Microsoft Build Engine (MSBuild)来增强其构建能力,特别是在处理WPF特有的任务时。 ... [详细]
  • 探讨了requireJS的domReady插件与window.onload事件在实际应用中的不同表现,特别是在处理页面加载过程中图片资源的影响。 ... [详细]
  • 深入理解SAP Fiori及其核心概念
    本文详细介绍了SAP Fiori的基本概念、发展历程、核心特性、应用类型、运行环境以及开发工具等,旨在帮助读者全面了解SAP Fiori的技术框架和应用场景。 ... [详细]
  • 本文详细介绍了如何使用 HTML 和 CSS 实现一个具有动画效果的手风琴组件,包括代码示例和实现原理。 ... [详细]
  • MVC框架下使用DataGrid实现时间筛选与枚举填充
    本文介绍如何在ASP.NET MVC项目中利用DataGrid组件增强搜索功能,具体包括使用jQuery UI的DatePicker插件添加时间筛选条件,并通过枚举数据填充下拉列表。 ... [详细]
author-avatar
VEACEN晨k
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有