Yii源码分析——CComponent

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)));
    }


//今天先写到这,回去再慢慢写。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值