`

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

阅读更多

 

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

 

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

 

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

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

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

public function onClicked($event)
{
    $this->raiseEvent('onClicked', $event);
}

 

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

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

$component->onClicked=$callback;

 

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

事件句柄的结构如下:

function methodName($event)
{
    ......
}

 

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

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

$component->onClicked=function($event) {
    ......
}

 

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

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

 

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

 

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

 

/**
     * 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 ):

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 表示事件是否已经被“解决”。

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

 

/**
     * 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,
     * <pre>
     * function handlerName($event) {}
     * </pre>
     * 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:
     * <pre>
     * $component->getEventHandlers($eventName)->add($eventHandler);
     * </pre>
     *
     * Using {@link getEventHandlers}, one can also specify the excution order
     * of multiple handlers attaching to the same event. For example:
     * <pre>
     * $component->getEventHandlers($eventName)->insertAt(0,$eventHandler);
     * </pre>
     * 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 事件触发时,会按顺序调用之前注册的事件处理器。

2
1
分享到:
评论
3 楼 silentime 2011-02-23  
呃,发现又一堆的格式代码,javaeye的富文本编辑器也不给力啊
2 楼 silentime 2011-02-23  
z.y.f 写道
男人,原来你在这里,你是在这里么?这里是你的主博客么?

目前算是吧,原来用新浪,但是新浪的代码引用太烂了,刚改不久
1 楼 z.y.f 2011-02-23  
男人,原来你在这里,你是在这里么?这里是你的主博客么?

相关推荐

    yii2-swoole:完整的解决方案,使yii2-framework与协程在swoole上运行

    yii2-swoole 为赋予 Yii2 框架协程异步能力而生。 后期开发会依赖 去实现功能,相信 Swoft 会是下一代优秀的框架。 此插件基于 swoole (v2.0) 底层实现的协程,改造 Yii2 的核心代码,使开发者无感知,以及在不改动...

    yii2.0 标签组件

    yii2.0 tags标签组件下载。

    yii2-settings:Yii2 设置组件

    Yii2 设置组件 安装 安装此扩展的首选方法是通过 。 要么跑 php composer.phar require --prefer-dist pendalf89/yii2-settings "*" 或添加 "pendalf89/yii2-settings": "*" 到composer.json文件的 require 部分...

    yii2-metronic:YII2的Metronic组件库

    这是一个基于的Yii2组件/小部件库。使用时需要先加载好Metronic的js和css资源。 安装 composer install evondu/yii2-metronic 使用方法 1、GrdiView 使用方法与原本YII2中的一样,其主要是修改了样式和布局,使它...

    yii2-components:Yii2框架的组件

    Yii2的组件Yii2框架的一些简单组件安装下载档案或克隆此存储库。 将包含文件的归档文件解压缩到路径/至/ project / components目录中。 如果它不存在,那么我们创建它。使用我们使用use指令进行连接。 让我们看一下...

    yii2elfinder:yii2elfinder

    yii2elfinder 感谢: : 感谢:zybodya 提供当前 yii 版本 yii2elfinder 介绍:旧版本无法使用,因为它完全不适用于最新的jquery版本! 所以除了行动,我不得不改变一切;) 这个扩展允许你将 ElFinder 文件管理...

    yii2-curl:用于 curl 的 Yii2 组件

    php composer.phar require --prefer-dist peterfrench/yii2-curl " * " 一旦 composer 安装了扩展,在您的配置文件中包含该组件。 'curl' =&gt; ['class' =&gt; 'peterfrench\curl\Curl' ,'options' =&gt; [/* curl options...

    yii2swoole让yii2运行在swoole上

    yii2 swoole:让yii2运行在swoole上 , 运行在swoole上的yii2是运行在php-fpm上yii2的5倍以上

    yii2-weui:为yii2封装weui组件

    yii2-weuiWeUI for Yii 2为Yii2封装weui组件,让微信开发更简单本组件为车卡通微信会员管理系统而做,]]authorfufudaoanu-zhangnuowei000InstallationInstall With ComposerThe preferred way to install this ...

    yii2-schemadump:从现有数据库生成模式

    yii2-schemadump 从现有数据库生成模式。演示版要求PHP 7.3或更高版本Yii 2.x安装composer require --dev jamband/yii2-schemadump用法在config / console.php中添加以下内容: return [ . . . 'components' =&gt; [ . ...

    Yii 2.0进阶版 高级组件 优化京东平台

    Yii 2.0进阶版 高级组件 优化京东平台 包括前后台源代码,使用php 实现数据库mysql

    yii-robokassa:用于与 Robokassa 支付服务的 api 配合使用的 Yii 组件

    用于与支付的 api 配合使用的 Yii 组件 安装 从这个 github 存储库下载 yii-robokassa: cd protected/components git clone https://github.com/ladamalina/yii-robokassa.git 在 protected/config/main.php 中...

    yii2sly:jquery 狡猾

    yii2sly 这个扩展是惊人的 jquery 滑块“sly”的包装器,可以在这里找到: 请。 仔细查看所有插件选项,可以通过将它们添加到“clientOptions”参数来传递这些选项,如下所示。 可以在此处找到扩展的演示: 安装 ...

    yii2cms:yii2cms

    Yii 2 高级应用模板 Yii 2 Advanced Application Template 是一个骨架 Yii 2 应用程序,最适合开发具有多层的复杂 Web 应用程序。 模板包括三层:前端、后端和控制台,每一层都是一个独立的 Yii 应用程序。 该模板...

    yii-qa:基于Yii2实现的问答系统

    Yii-QA简介(此项目目前已不再维护)感谢选择Yii-QA,基于框架基础实现的问答程序。 #意识到目前的急性时间有限,无法管理太多的额外项目,我准备合并现有手上的项目,集成在一个项目中,感谢支持!!!!!!!请关注:...

    Yii框架应用组件用法实例分析

    本文实例讲述了Yii框架应用组件用法。分享给大家供大家参考,具体如下: 应用组件 ¶ 应用主体是服务定位器, 它部署一组提供各种不同功能的 应用组件 来处理请求。 例如,urlManager组件负责处理网页请求路由到对应...

    swoft-yii2:swift的yii2-component

    此插件依赖于 实现已支持的组件yii2-db 易框架的 MySQL-Connect 、 ActiveRecord 等yii2-log 易框架的日志组件,推荐还是用 swoft-log 吧yii2-cache 易框架的缓存组件,安装环境要求swoft-v1.0 以上composer install...

    yii2fullcalendar:jQuery Fullcalendar Yii2扩展

    yii2fullcalendar JQuery Fullcalendar Yii2扩展JQuery来自: ://arshaw.com/fullcalendar/版本4.0.2许可证MIT jQuery文档: //arshaw.com/fullcalendar/docs/ Yii2扩展,通过 可以在这里找到一个小样本:http: ...

    yii-passport:使Laravel Passport与Yii一起工作

    Yii护照 安装 :light_bulb: 这是展示如何安装软件包的好地方,请参见下文: 跑步 $ composer require inquid/yii-passport 用法 :light_bulb: 这是显示一些用法示例的好地方! 变更日志 请看看 。 贡献 请看看 。...

    yii2-fullcalendar:Yii 2组件,可轻松实现全日历集成

    Yii2全日历组件 安装 安装此扩展的首选方法是通过 。 要安装,请运行 $ php composer.phar require edofre/yii2-fullcalendar "V1.0.11" 或添加 "edofre/yii2-fullcalendar": "V1.0.11" 到composer.json文件的...

Global site tag (gtag.js) - Google Analytics