首页 > 代码库 > 组件和事件行为管理

组件和事件行为管理

Yii是一个基于组件、用于开发大型 Web 应用的高性能 PHP 框架。CComponent几乎是所有类的基类,它控制着组件与事件的管理,其方法与属性如下,私有变量$_e数据存放事件(evnet,有些地方叫hook),$_m数组存放行为(behavior)。



组件管理

YII是一个纯oop框架,很多类中的成员变量的受保护或者私有的,CComponent中利用php中的魔术方法__get(),__set()来访问和设置属性,但这些方法的作用远不指这些。下面用__get()来说明

[php] view plaincopyprint?

  1. public function __get($name)  

  2. {  

  3.    $getter=‘get‘.$name;  

  4.    if(method_exists($this,$getter))  

  5.        return $this->$getter();  

  6.    else if(strncasecmp($name,‘on‘,2)===0 && method_exists($this,$name))  

  7.    {  

  8.        // duplicating getEventHandlers() here for performance

  9.        $name=strtolower($name);  

  10.        if(!isset($this->_e[$name]))  

  11.            $this->_e[$name]=new CList;  

  12.        return $this->_e[$name];  

  13.    }  

  14.    else if(isset($this->_m[$name]))  

  15.        return $this->_m[$name];  

  16.    else if(is_array($this->_m))  

  17.    {  

  18.        foreach($this->_m as $object)  

  19.        {  

  20.            if($object->getEnabled() && (property_exists($object,$name) || $object->canGetProperty($name)))  

  21.                return $object->$name;  

  22.        }  

  23.    }  

  24.    throw new CException(Yii::t(‘yii‘,‘Property "{class}.{property}" is not defined.‘,  

  25.        array(‘{class}‘=>get_class($this), ‘{property}‘=>$name)));  

  26. }  

public function __get($name)
{
    $getter=‘get‘.$name;
    if(method_exists($this,$getter))
        return $this->$getter(); 
    else if(strncasecmp($name,‘on‘,2)===0 && method_exists($this,$name))
    {
        // duplicating getEventHandlers() here for performance
        $name=strtolower($name);
        if(!isset($this->_e[$name]))
            $this->_e[$name]=new CList;
        return $this->_e[$name];
    }
    else if(isset($this->_m[$name]))
        return $this->_m[$name];
    else if(is_array($this->_m))
    {
        foreach($this->_m as $object)
        {
            if($object->getEnabled() && (property_exists($object,$name) || $object->canGetProperty($name)))
                return $object->$name;
        }
    }
    throw new CException(Yii::t(‘yii‘,‘Property "{class}.{property}" is not defined.‘,
        array(‘{class}‘=>get_class($this), ‘{property}‘=>$name)));
}

当CComponent或者其子类对象实例$obj->name的时候,__get($name)方法:
1、首先判断实例中是否有getName()方法,如果有则返回 ,如果没有执行第2步
2、判断是否是以on开头的,以on开头的一般都是CComponent子类中预留的事件,用与挂在事件,通过method_exists($this,$name)判断该name是否存在类的实例中,如果存在,返回事件,否则执行第3步
3、如果name存在行为数组中,返回改行为,如果不存在,执行第4步
4、遍历行为数组,数组中行为是CBehavior子类的实例,而CBehavior又是CComponent中子类,所以用递归的方法获取行为中的方法,如果没有,执行第5步
5、抛出异常:请求的属性不存在。

在CComponent子类中可以重载__get()方法,如在CModule中加入了获取组件的判断。这就注意一个问题了属性和组件名最好不要重名,因为程序会优先加载组件,可能得到的不是我们想要的属性,如果必须重名的话,就要用getter获取属性。

[php] view plaincopyprint?

  1. public function __get($name)  

  2. {  

  3.    if($this->hasComponent($name))  

  4.        return $this->getComponent($name);  

  5.    else

  6.        return parent::__get($name);  

  7. }  

public function __get($name)
{
    if($this->hasComponent($name))
        return $this->getComponent($name);
    else
        return parent::__get($name);
}

关于组件的加载与创建,上篇YII框架分析笔记1:YII执行流程中的第3点中有个疑问:注册框架核心组件的时候一下子加载这么多,是不是影响性能呢?其实没有,注册的时候只是把组件和其对应的配置用键值对的形式保存在数组中(预加载的除外),当用到时候才像上面那样去创建组件,会通过YIIBase中的createComponent()方法创建,并初始化。通过CModule或其子孙类(如CWebApplication)调用__get()或getComponent()获取组件时,CModule通过$_components数组建立对象池,确保每个组件在一次请求中只实例化一次。


事件行为管理

事件相当于对一个组件的扩展或者插件,以组件中预留的钩子实现组件内部调用外部、外部对组件部分控制。在CComponent子类中可以定义以on开头的方法为事件,类似于js中的onclick、onchange等,其实原理差不多。所有事件是与CComponent在同一文件中CEvent的子类。

[php] view plaincopyprint?

  1. /**

  2. * Raised right BEFORE the application processes the request.

  3. * @param CEvent $event the event parameter

  4. */

  5. public function onBeginRequest($event)  

  6. {  

  7.    $this->raiseEvent(‘onBeginRequest‘,$event);  

  8. }  

  9. /**

  10. * Runs the application.

  11. * This method loads static application components. Derived classes usually overrides this

  12. * method to do more application-specific tasks.

  13. * Remember to call the parent implementation so that static application components are loaded.

  14. */

  15. public function run()  

  16. {  

  17.    if($this->hasEventHandler(‘onBeginRequest‘))  

  18.        $this->onBeginRequest(new CEvent($this));  

  19.    $this->processRequest();  

  20.    if($this->hasEventHandler(‘onEndRequest‘))  

  21.        $this->onEndRequest(new CEvent($this));  

  22. }  

/**
* Raised right BEFORE the application processes the request.
* @param CEvent $event the event parameter
*/
public function onBeginRequest($event)
{
    $this->raiseEvent(‘onBeginRequest‘,$event);
}
/**
* Runs the application.
* This method loads static application components. Derived classes usually overrides this
* method to do more application-specific tasks.
* Remember to call the parent implementation so that static application components are loaded.
*/
public function run()
{
    if($this->hasEventHandler(‘onBeginRequest‘))
        $this->onBeginRequest(new CEvent($this));
    $this->processRequest();
    if($this->hasEventHandler(‘onEndRequest‘))
        $this->onEndRequest(new CEvent($this));
}

比如在CApplication中调用run()方法在处理请求之前先判断外部是否传人onBeginRequest事件的句柄,如果有则通过onBeginRequest($event)方法调用CComponent中的raiseEvent()方法执行句柄中的函数或者方法。

行为是事件的升级版,所有的行为都是CBehavior的子类。分析上面的__get()方法分析第4步可以看出来行为的作用是完全扩展组件的特性,可以是属性、方法、事件甚至行为,这样使程序开发更加灵活。

行为的另一个作用是将相似事件句柄放在一起,在行为执行attach()方法的时候会将events()方法中返回的事件句柄绑定,这样做达到方面管理和扩展的目的。比如CModelBehavior中将model相关的事件集中起来,便于其子类的复用,当我们需求为model添加行为的时候可以继承它。