你现在的位置:首页 > PHP网站建设知识库 > Yii > 正文

Yii代码分析:collections

framework/collections包 主要包含一些常用的数据结构,有  
CTypeList  
CTypeMap  
CAttribute  
Collection  
CConfiguration  
CList  
CMap  
CQueue  
CStack  
CListIterator  
CMapIterator  
CQueueIterator  
CStackIterator1.CList   
1.CList   把数组(数字索引)与对象结合使用。  
$r[] = 'test';与$r->add('test')我觉得后者较好理解。  
CList实现了几个接口,ArrayAccess Countable IteratorAggregate,  
让$r数组对象,既能使用foreach循环读取(自定义的数组,而不是本来的只能遍历对象的公开属性)、又能使用count()函数,还能使用$r[]的方式访问。  
CList的getIterator返回的是CListIterator,这个只是实现了Iterator接口。  
  
2.CMap 把数组(字符串或数字索引)与对象结合使用  
CMap与CList实现的接口一样。  
  
CMapIterator与CListIterator类似,但是有点不同,CListIterator是数字索引,所以遍历的时候,可以用类似$i++之类的来移动指针,CMapIterator则不行。  
CMapIterator的思路是把keys用array_keys取出来,放进一个数组,然后把数字和keys对应,遍历的问题就解决了。  
  
3.CQueue 实现的接口 IteratorAggregate,Countable, 实现了几个方法dequeue() enqueue() peek(),靠的是array_shift()和array_push()两个方法。  
CQueueIterator 与CListIterator没多大区别。  
  
4.CStack与CQueue类似,只不过它是实现栈,实现pop() push() peek()方法,靠的是array_pop(),array_push()两个方法。  
  
5.CTypeList是CList的加强版,它限制这个列表的item必须是某种类型的。  
6.CTypeMap是CMap的加强版,它限制这个列表的item必须是某种类型的。  
  
7.CConfiguration类似CMap,它专门处理配置信息的,从文件读取配置数组(loadFromFile()),并把它绑定到相应的对象上(applyTo())。  
------------------------------------------------------------------------------------------------------------------------------  
上面是之前写的笔记,今天整理整理,下面结合源码的接口讨论讨论  
1.CList  
class CList extends CComponent implements IteratorAggregate,ArrayAccess,Countable  
{  
    private $_d=array();  //数据的容器,php里我目前知道的最多用来存临时数据的容器就只有数组,不知道有别的结构没。人家起名也够简洁的,一个d代表data  
    private $_c=0;        //一看就懂,count,数据数组里有多少个元素,这个值会在增删的时候实时变化。  
    private $_r=false;    //这个像个只读锁,可以把数据放到容器里面去,然后加个锁(有个setReadOnly()方法),  
                                    //此后在它的生存周期内容器内的数据就是只读的啦,后面的insertAt()和removeAt()方法都会去判断是否有加锁。  
        /**构造器,可以在构造的时候传入数据,并可以设置是否加只读锁*/  
    public function __construct($data=null,$readOnly=false)  
    {  
        if($data!==null)  
            $this->copyFrom($data);//copyFrom,作用就是把一个数据给拷到本身的数据容器数组里去,如果提供的是CList实例,就把它的数据导过来  
        $this->setReadOnly($readOnly);  
    }  
        /**是否只读*/  
    public function getReadOnly()  
    {  
        return $this->_r;  
    }  
  
    /** 
     * 加个锁 
     */  
    protected function setReadOnly($value)  
    {  
        $this->_r=$value;  
    }  
  
    /** 
     * 返回一个Iterator遍历器,实现IteratorAggregate用的。遍历器用来干嘛的,foreach用的 
     */  
    public function getIterator()  
    {  
        return new CListIterator($this->_d);  
    }  
  
    /** 
     * 元素总数,主要是因为实现Countable接口,可以用count($clist_instance) 
     */  
    public function count()  
    {  
        return $this->getCount();  
    }  
  
    /** 
     * 功能同count,返回元素总数 
     */  
    public function getCount()  
    {  
        return $this->_c;  
    }  
  
    /** 
     * 跟offsetGet方法一模一样 
           */  
    public function itemAt($index)  
    {  
        if(isset($this->_d[$index]))  
            return $this->_d[$index];  
        else if($index>=0 && $index<$this->_c) // in case the value is null  
            return $this->_d[$index];  
        else  
            throw new CException(Yii::t('yii','List index "{index}" is out of bound.',  
                array('{index}'=>$index)));  
    }  
  
    /** 
     * 末尾加个元素 
     */  
    public function add($item)  
    {  
        $this->insertAt($this->_c,$item);  
        return $this->_c-1;  
    }  
  
    /** 
     * 根据数字索引插入一个元素,这里主要用到了array_aplice,这个方法牛逼啊,删除替换都能搞,有大牛告诉我它内部怎么实现的吗? 
     */  
    public function insertAt($index,$item)  
    {  
        if(!$this->_r)  
        {  
            if($index===$this->_c)  
                $this->_d[$this->_c++]=$item;  
            else if($index>=0 && $index<$this->_c)  
            {  
                array_splice($this->_d,$index,0,array($item));  
                $this->_c++;  
            }  
            else  
                throw new CException(Yii::t('yii','List index "{index}" is out of bound.',  
                    array('{index}'=>$index)));  
        }  
        else  
            throw new CException(Yii::t('yii','The list is read only.'));  
    }  
  
    /** 
     * 根据数字索引删除一个元素, 
     */  
    public function remove($item)  
    {  
        if(($index=$this->indexOf($item))>=0)  
        {  
            $this->removeAt($index);  
            return $index;  
        }  
        else  
            return false;  
    }  
  
    /** 
     * remove的实现,操作前判断是否加锁,看,这里又是用array_splice 
     */  
    public function removeAt($index)  
    {  
        if(!$this->_r)  
        {  
            if($index>=0 && $index<$this->_c)  
            {  
                $this->_c--;  
                if($index===$this->_c)  
                    return array_pop($this->_d);  
                else  
                {  
                    $item=$this->_d[$index];  
                    array_splice($this->_d,$index,1);  
                    return $item;  
                }  
            }  
            else  
                throw new CException(Yii::t('yii','List index "{index}" is out of bound.',  
                    array('{index}'=>$index)));  
        }  
        else  
            throw new CException(Yii::t('yii','The list is read only.'));  
    }  
  
    /** 
     * 清空数据,之前搞不懂为啥不直接根据是否有加锁进行操作,没加锁的话,直接把$_d清掉和$_c设置为0不就完了吗?不过现在想想,它这么做还是有道理的,把删除的操作交给删除的接口去干, 
           * 那么如果在删除接口操作的时候,它有实现一些其它的钩或者其它的逻辑,那些逻辑就能正确调用! 
     */  
    public function clear()  
    {  
        for($i=$this->_c-1;$i>=0;--$i)  
            $this->removeAt($i);  
    }  
  
    /** 
     * 查查数据容器里有没有这一项,跟查花名册似的 
     */  
    public function contains($item)  
    {  
        return $this->indexOf($item)>=0;  
    }  
  
    /** 
     * 上面contains的实现,数组的查询这里用到了array_search,如果只是查是否有某个key的话,用isset就行了,这里是查value 
     */  
    public function indexOf($item)  
    {  
        if(($index=array_search($item,$this->_d,true))!==false)  
            return $index;  
        else  
            return -1;  
    }  
  
    /** 
     *返回数据容器 
     */  
    public function toArray()  
    {  
        return $this->_d;  
    }  
  
    /** 
     * 从比的地方把数据倒过来,如果原来这里有数据,会先清掉 
     */  
    public function copyFrom($data)  
    {  
        if(is_array($data) || ($data instanceof Traversable))  
        {  
            if($this->_c>0)  
                $this->clear();  
//这里有个问题,我在这如果加一行var_dump($data->_d);会报错,提示Trying to get property of non-object   
//可是var_dump($data);明明是个CList对象,是否$data经过instanceof CList之后才知道它是个CList对象(貌似不是这样的),求高手解答  
            if($data instanceof CList)    
                $data=$data->_d;      
            foreach($data as $item)  
                $this->add($item);  
        }  
        else if($data!==null)  
            throw new CException(Yii::t('yii','List data must be an array or an object implementing Traversable.'));  
    }  
    /** 
     * 其实就是把另一个数组追加到本数组容器里。 
     */  
    public function mergeWith($data)  
    {  
        if(is_array($data) || ($data instanceof Traversable))  
        {  
            if($data instanceof CList)  
                $data=$data->_d;  
            foreach($data as $item)  
                $this->add($item);  
        }  
        else if($data!==null)  
            throw new CException(Yii::t('yii','List data must be an array or an object implementing Traversable.'));  
    }  
  
    /** 
     * ArrayAccess接口的实现 
     */  
    public function offsetExists($offset)  
    {  
        return ($offset>=0 && $offset<$this->_c);  
    }  
  
    /** 
     * 这里跟itemAt()一样,为什么不直接把itemAt()代码放这就行了?原因跟之前的clear()方法里面有点像,相当于增删改查的逻辑交给一个代理,这个代理有权限控制。下面offsetSet、offsetUnset也是一样。 
     */  
    public function offsetGet($offset)  
    {  
        return $this->itemAt($offset);  
    }  
  
    /** 
     * 同上 
     */  
    public function offsetSet($offset,$item)  
    {  
        if($offset===null || $offset===$this->_c)  
            $this->insertAt($this->_c,$item);  
        else  
        {//这里本来可以直接在$_d[$offset] = $item;但交给代理去处理就多了一层权限控制  
            $this->removeAt($offset);  
            $this->insertAt($offset,$item);  
        }  
    }  
  
    /** 
     * 同上 
     */  
    public function offsetUnset($offset)  
    {  
        $this->removeAt($offset);  
    }  
}