• 技术文章 >php教程 >PHP开发

    Yii框架分析(二)——CComponent类剖析

    黄舟黄舟2016-12-27 11:04:05原创499
    Yii是基于组件(component-based)的web框架,CComponent类是所有组件的基类。

    CComponent类为子类提供了基于属性(property)、事件(event)、行为(behavior)编程接口。

    1.组件的属性(property)

    Ccomponent类并没有提供属性的变量存储,需要由子类来提供两个方法来实现。子类的getPropertyName()方法提供$component->PropertyName的取值操作数据,子类的setPropertyName($val)方法提供$component->PropertyName赋值操作。

    $width=$component->textWidth;???? // 获取 textWidth 属性

    实现方式为调用子类提供的方法 $width=$component->getTextWidth()

    $component->textWidth=$width;???? // 设置 textWidth 属性

    实现方式为调用子类提供的方法 $component->setTextWidth($width)

    public function getTextWidth()
    {
        return $this->_textWidth;
    }
     
    public function setTextWidth($value)
    {
        $this->_textWidth=$value;
    }

    组件的属性值是大小写不敏感的(类的成员时大小写敏感的)

    2.组件的事件(event)

    组件事件是一种特殊的属性,它可以将事件处理句柄(可以是函数名、类方法或对象方法)注册(绑定)到一个事件名上,句柄在事件被唤起的时候被自动调用。
    组件事件存放在CComponent 的$_e[]数组里,数组的键值为事件的名字,键值的数值为一个Clist对象,Clist是Yii提供的一个队列容器,Clist的方法add()添加事件的回调handle。

    //添加一个全局函数到事件处理
    $component-> onBeginRequest=”logRequest”;
    //添加一个类静态方法到事件处理
    $component-> onBeginRequest=array(“CLog”,” logRequest”);
    //添加一个对象方法到事件处理
    $component-> onBeginRequest=array($mylog,” logRequest”);

    唤起事件:
    $component ->raiseEvent(‘onBeginRequest ‘, $event);
    会自动调用:
    logRequest($event), Clog:: logRequest($event)和$mylog.logRequest($event)

    事件句柄必须按照如下来定义 :

    function methodName($event)
    {
    ……
    }
    $event 参数是 CEvent 或其子类的实例,它至少包含了”是谁挂起了这个事件”的信息。

    事件的名字以”on”开头,在__get()和__set()里可以通过这个来区别属性和事件。

    3.组件行为(behavior)

    组件的行为是一种不通过继承而扩展组件功能的方法(参见设计模式里的策略模式)。

    行为类必须实现 IBehavior 接口,大多数行为可以从 CBehavior 基类扩展而来。

    IBehavior接口提供了4个方法。
    attach($component)将自身关联到组件,detach($component) 解除$component关联,getEnabled()和setEnabled()设置行为对象的有效性。

    行为对象存放在组件的$_m[]数组里,数组键值为行为名字符串,数组值为行为类对象。

    组件通过attachBehavior ($name,$behavior)来扩展一个行为:
    $component-> attachBehavior (‘render’,$htmlRender)
    为$component添加了一个名字为render的行为,$htmlRender 需是一个实现 IBehavior 接口的对象,或是一个数组:

    array( ‘class’=>’path.to.BehaviorClass’,
        ‘property1′=>’value1′,
        ‘property2′=>’value2′,
    * )

    会根据数组的class来创建行为对象并设置属性值。

    $htmlRender被存储到$_m[‘render’]中。

    外部调用一个组件未定义的方法时,魔术方法__call()?会遍历所有行为对象,如果找到同名方法就调用之。

    例如?$htmlRender?有个方法?renderFromFile(),则可以直接当做组件的方法来访问:

    $component-> renderFromFile ()

    4.CComponent源码分析

    //所有部件的基类
    class CComponent
    {
        private $_e;
        private $_m;
     
        //获取部件属性、事件和行为的magic method
        public function __get($name)
        {
            $getter=’get’.$name;
            //是否存在属性的get方法
            if(method_exists($this,$getter))
                return $this->$getter();
            //以on开头,获取事件处理句柄
            else if(strncasecmp($name,’on’,2)===0 && method_exists($this,$name))
            {
                // 事件名小写
                $name=strtolower($name);
                // 如果_e[$name] 不存在,返回一个空的CList事件句柄队列对象
                if(!isset($this->_e[$name]))
                    $this->_e[$name]=new CList;
                // 返回_e[$name]里存放的句柄队列对象
                return $this->_e[$name];
            }
            // _m[$name] 里存放着行为对象则返回
            else if(isset($this->_m[$name]))
                return $this->_m[$name];
            else
                throw new CException(Yii::t(‘yii’,'Property “{class}.{property}” is not defined.’,
                    array(‘{class}’=>get_class($this), ‘{property}’=>$name)));
        }
     
        /**
        * PHP magic method
        * 设置组件的属性和事件
        */
         public function __set($name,$value)
        {
            $setter=’set’.$name;
            //是否存在属性的set方法
            if(method_exists($this,$setter))
                $this->$setter($value);
            //name以on开头,这是事件处理句柄
            else if(strncasecmp($name,’on’,2)===0 && method_exists($this,$name))
            {
                // 事件名小写
                $name=strtolower($name);
                // _e[$name] 不存在则创建一个CList对象
                if(!isset($this->_e[$name]))
                    $this->_e[$name]=new CList;
                // 添加事件处理句柄
                $this->_e[$name]->add($value);
            }
            // 属性没有set方法,只有get方法,为只读属性,抛出异常
            else if(method_exists($this,’get’.$name))
                throw new CException(Yii::t(‘yii’,'Property “{class}.{property}” is read only.’,
                    array(‘{class}’=>get_class($this), ‘{property}’=>$name)));
            else
                throw new CException(Yii::t(‘yii’,'Property “{class}.{property}” is not defined.’,
                    array(‘{class}’=>get_class($this), ‘{property}’=>$name)));
        }
     
        /**
        * PHP magic method
        * 为isset()函数提供是否存在属性和事件处理句柄的判断
        */
        public function __isset($name)
        {
            $getter=’get’.$name;
            if(method_exists($this,$getter))
                return $this->$getter()!==null;
            else if(strncasecmp($name,’on’,2)===0 && method_exists($this,$name))
            {
                $name=strtolower($name);
                return isset($this->_e[$name]) && $this->_e[$name]->getCount();
            }
            else
                return false;
        }
     
        /**
        * PHP magic method
        * 设置属性值为空或删除事件名字对应的处理句柄
        */
        public function __unset($name)
        {
            $setter=’set’.$name;
            if(method_exists($this,$setter))
                $this->$setter(null);
            else if(strncasecmp($name,’on’,2)===0 && method_exists($this,$name))
                unset($this->_e[strtolower($name)]);
            else if(method_exists($this,’get’.$name))
                throw new CException(Yii::t(‘yii’,'Property “{class}.{property}” is read only.’,
            array(‘{class}’=>get_class($this), ‘{property}’=>$name)));
        }
     
        /**
        * PHP magic method
        *?CComponent未定义的类方法,寻找行为类里的同名方法,实现行为方法的调用
        */
        public function __call($name,$parameters)
        {
            // 行为类存放的$_m数组不空
            if($this->_m!==null)
            {
                // 循环取出$_m数组里存放的行为类
                foreach($this->_m as $object)
                {
                    // 行为类对象有效,并且方法存在,调用之
                    if($object->enabled && method_exists($object,$name))
                        return call_user_func_array(array($object,$name),$parameters);
                }
            }
            throw new CException(Yii::t(‘yii’,'{class} does not have a method named “{name}”.’,
                array(‘{class}’=>get_class($this), ‘{name}’=>$name)));
        }
     
        /**
        * 根据行为名返回行为类对象
        */
        public function asa($behavior)
        {
            return isset($this->_m[$behavior]) ? $this->_m[$behavior] : null;
        }
     
        /**
        * Attaches a list of behaviors to the component.
        * Each behavior is indexed by its name and should be an instance of
        *?{@link?IBehavior}, a string specifying the behavior class, or an
        * array of the following structure:
        *
    * array( *???? ‘class’=>’path.to.BehaviorClass’, *???? ‘property1′=>’value1′, *???? ‘property2′=>’value2′, * ) *

    * @param array list of behaviors to be attached to the component * @since 1.0.2 */ public function attachBehaviors($behaviors) { // $behaviors为数组 $name=>$behavior foreach($behaviors as $name=>$behavior) $this->attachBehavior($name,$behavior); } /** * 添加一个行为到组件 */ public function attachBehavior($name,$behavior) { /* $behavior不是IBehavior接口的实例,则为 * array( *???? ‘class’=>’path.to.BehaviorClass’, *???? ‘property1′=>’value1′, *???? ‘property2′=>’value2′, * ) * 传递给Yii::createComponent创建行为了并初始化对象属性 */ if(!($behavior instanceof IBehavior)) $behavior=Yii::createComponent($behavior); $behavior->setEnabled(true); $behavior->attach($this); return $this->_m[$name]=$behavior; } /** * 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); // _e[$name] 事件处理句柄队列存在 if(isset($this->_e[$name])) { // 循环取出事件处理句柄 foreach($this->_e[$name] as $handler) { // 事件处理句柄为全局函数 if(is_string($handler)) call_user_func($handler,$event); else if(is_callable($handler,true)) { // an array: 0 – object, 1 – method name list($object,$method)=$handler; if(is_string($object))?// 静态类方法 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)))); // $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))); } }

    以上就是Yii框架分析(二)——CComponent类剖析的内容,更多相关内容请关注PHP中文网(m.sbmmt.com)!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    上一篇:Laravel模板引擎Blade中section的一些标签的区别介绍 下一篇:Yii框架分析(三)——类加载机制及应用组件的管理、配置、访问、创建
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【腾讯云】年中优惠,「专享618元」优惠券!• linux awk命令详解• linux awk命令详解• php 应用程序安全防范技术研究• Yii快速入门 (二)• Symfony2实现从数据库获取数据的方法小结
    1/1

    PHP中文网