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); } } ==============================================还没写完,有空接着写=====================================