Yii源码分析——CComponent

来源:互联网 发布:风云排名知乎 编辑:程序博客网 时间:2024/05/01 07:53

CComponent用到许多php的魔术方法。

下面摘自php手册的一些魔术方法的说明。

属性重载void__set (string $name , mixed$value )mixed__get (string $name )bool __isset ( string$name )void__unset (string $name )在给未定义的变量赋值时,__set() 会被调用。读取未定义的变量的值时,__get() 会被调用。当对未定义的变量调用isset() 或empty()时,__isset()会被调用。当对未定义的变量调用unset()时,__unset()会被调用。参数$name是指要操作的变量名称。__set()方法的$value 参数指定了$name变量的值。属性重载只能在对象中进行。在静态方法中,这些魔术方法将不会被调用。所以这些方法都不能被 声明为static。
方法重载mixed __call ( string $name , array $arguments )mixed __callStatic ( string $name , array $arguments )当调用一个不可访问方法(如未定义,或者不可见)时,__call() 会被调用。当在静态方法中调用一个不可访问方法(如未定义,或者不可见)时,__callStatic() 会被调用。$name参数是要调用的方法名称。$arguments参数yx是一个数组,包含着要传递给方法的参数。CComponent    private $_e;//存放事件的数组     private $_m;//存放行为的数组总结下: 假设 $c = new CComponent; 访问 $c->abc 时,逻辑如下://注意,在访问对象的属性或者方法时,会按继承链去回溯访问,因为这里CComponent没有继承任何类,所以不考虑这因素。1.判断当前的对象是否有可见的属性public $abc;(protected/private不行,因为对外部不可见),有则返回,没有进行第2步,即执行__get()的逻辑;2.判断当前对象是否有方法pubic/protected/private function getabc() 有则执行该方法,否则执行第3步;3.判断是否是访问以'on'开头的属性或者方法,如果是而且该对象有这个方法,则判断事件数组里是否有这个事件,没有就往事件数组添加一个$this->_e[$name]=new CList;并返回这个CList,有就直接返回以这个事件命名的事件列表。CList后面介绍,它继承自CComponent,而且实现了几个接口,可以当做数组操作。如果不是‘on’开头,则进行第4步;4.判断abc是否存放在对象的行为数组$_m里,有就返回,没有就判断行为数组$_m是否为空,不过不为空,就遍历这个行为数组,分别用1~4的规则去判断这些行为里面有没这个属性,有就返回,最后如果还没有就抛出异常。靠,尼玛写这么复杂判断逻辑有什么用?其实这是牺牲一点执行效率换取开发的灵活性和开发效率罢了。就相当于有了个映射,以后约定一个代码规范,要获取$c->abc,abc既可以定义为对象的属性,也可以写个function getabc()去返回。如果是写$c->onabc,这种on开头的,就去访问事件列表。最后再去考虑abc是否为对象的一个行为。关于行为,Yii的IBehavior接口的说明是这样: * A behavior is a way to enhance a component with additional methods that  * are defined in the behavior class and not available in the component class.就是说,可以把一些方法绑定到一些对象上去,就跟老虎插了两个翅膀一样牛逼了。这点有点像javascript中,对象的方法是可以动态绑定到别的对象上使用一样!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)));    }刚才说的把方法绑定到对象上,是靠什么做到的?靠这个call_user_func_array(array($object,$name),$parameters);一个简单的思路,就是把当前的CComponent对象作为$parameters数组里的一个成员传过去$object行为对象,让它操作CComponent对象。举个形象的例子,假如我是CComponent对象,我遇到一道数学题不会做,但是我跟同学吹牛逼说我能把数学题解出来。这下怎么办呢,这时候我加载一个解题Behavior,它能告诉我怎么利用已学的一些数学公式(这些公式都在我脑子里了,只是我不知怎么去用它),最后把题给解出来。好了,这是后同学就问我,小灰马,你会解那道题吗?(这相当于访问了 $小灰马->会解数学题吗()这个方法),我肯定是马上去call我的behavior教我怎么搞,搞完我就返回答案给同学,同学说:“哇,小灰马好牛逼啊!”。CComponent的 __call()方法就会用到行为这个东西。当访问$c->fn()时,逻辑如下://注意,类的方法访问还是会按照继承关系去回溯访问的,下面不提这个,隐含在里面。1.判断$c是否有这个方法,有则执行,没有则执行第2步;2.循环$c的行为数组,判断行为是否有这个方法,有就用这个行为的方法去调用,返回,当遍历完整个行为数组还没有的话,执行第3步;3.判断$c是否有个属性,它的值是一个匿名函数,有就去执行它。(Closure是php 5.3.0及以后的版本才有的!参考php手册。),没有就抛出异常。    public function __call($name,$parameters)    {        if($this->_m!==null)        {            foreach($this->_m as $object)            {                if($object->getEnabled() && method_exists($object,$name))                    return call_user_func_array(array($object,$name),$parameters);            }        }        if(class_exists('Closure', false) && $this->canGetProperty($name) && $this->$name instanceof Closure)            return call_user_func_array($this->$name, $parameters);        throw new CException(Yii::t('yii','{class} and its behaviors do not have a method or closure named "{name}".',            array('{class}'=>get_class($this), '{name}'=>$name)));    }//今天先写到这,回去再慢慢写。


原创粉丝点击