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














你可能感兴趣的:(list,function,Collections,Integer,iterator,interface)