PHP实现Collection数据集类及其原理
PHP 语言最重要的特性之一便是数组了(特别是关联数组)。
PHP 为此也提供不少的函数和类接口方便于数组操作,但没有一个集大成的类专门用来操作数组。
如果数组操作不多的话,个别函数用起来会比较灵活,开销也小。
但是,如果经常操作数组,尤其是对数组进行各种操作如排序、入栈、出队列、翻转、迭代等,系统函数用起来可能就没有那么优雅了。
下面已实现的一个 Collection 类(数据集对象),来自 ThinkPHP5.0 的基础类 Collection,就是一个集大成的类。
源码确实不错,也不是特别长,就全贴上了,方便阅读。跳到下面的例子结合看会比较好理解。
namespacethink;useArrayAccess;useArrayIterator;useCountable;useIteratorAggregate;useJsonSerializable;classCollectionimplementsArrayAccess,Countable,IteratorAggregate,JsonSerializable{protected$items=[];publicfunction__construct($items=[]){$this->items=$this->convertToArray($items);}publicstaticfunctionmake($items=[]){returnnewstatic($items);}publicfunctionisEmpty(){returnempty($this->items);}publicfunctiontoArray(){returnarray_map(function($value){return($valueinstanceofModel||$valueinstanceofself)?$value->toArray():$value;},$this->items);}publicfunctionall(){return$this->items;}publicfunctionmerge($items){returnnewstatic(array_merge($this->items,$this->convertToArray($items)));}/***比较数组,返回差集*/publicfunctiondiff($items){returnnewstatic(array_diff($this->items,$this->convertToArray($items)));}/***交换数组中的键和值*/publicfunctionflip(){returnnewstatic(array_flip($this->items));}/***比较数组,返回交集*/publicfunctionintersect($items){returnnewstatic(array_intersect($this->items,$this->convertToArray($items)));}publicfunctionkeys(){returnnewstatic(array_keys($this->items));}/***删除数组的最后一个元素(出栈)*/publicfunctionpop(){returnarray_pop($this->items);}/***通过使用用户自定义函数,以字符串返回数组**@paramcallable$callback*@parammixed$initial*@returnmixed*/publicfunctionreduce(callable$callback,$initial=null){returnarray_reduce($this->items,$callback,$initial);}/***以相反的顺序返回数组。*/publicfunctionreverse(){returnnewstatic(array_reverse($this->items));}/***删除数组中首个元素,并返回被删除元素的值*/publicfunctionshift(){returnarray_shift($this->items);}/***把一个数组分割为新的数组块.*/publicfunctionchunk($size,$preserveKeys=false){$chunks=[];foreach(array_chunk($this->items,$size,$preserveKeys)as$chunk){$chunks[]=newstatic($chunk);}returnnewstatic($chunks);}/***在数组开头插入一个元素*/publicfunctionunshift($value,$key=null){if(is_null($key)){array_unshift($this->items,$value);}else{$this->items=[$key=>$value]+$this->items;}}/***给每个元素执行个回调*/publicfunctioneach(callable$callback){foreach($this->itemsas$key=>$item){if($callback($item,$key)===false){break;}}return$this;}/***用回调函数过滤数组中的元素*/publicfunctionfilter(callable$callback=null){if($callback){returnnewstatic(array_filter($this->items,$callback));}returnnewstatic(array_filter($this->items));}/***返回数组中指定的一列*/publicfunctioncolumn($column_key,$index_key=null){if(function_exists('array_column')){returnarray_column($this->items,$column_key,$index_key);}$result=[];foreach($this->itemsas$row){$key=$value=null;$keySet=$valueSet=false;if(null!==$index_key&&array_key_exists($index_key,$row)){$keySet=true;$key=(string)$row[$index_key];}if(null===$column_key){$valueSet=true;$value=$row;}elseif(is_array($row)&&array_key_exists($column_key,$row)){$valueSet=true;$value=$row[$column_key];}if($valueSet){if($keySet){$result[$key]=$value;}else{$result[]=$value;}}}return$result;}/***对数组排序*/publicfunctionsort(callable$callback=null){$items=$this->items;$callback?uasort($items,$callback):uasort($items,function($a,$b){if($a==$b){return0;}return($a<$b)?-1:1;});returnnewstatic($items);}/***将数组打乱*/publicfunctionshuffle(){$items=$this->items;shuffle($items);returnnewstatic($items);}/***截取数组*/publicfunctionslice($offset,$length=null,$preserveKeys=false){returnnewstatic(array_slice($this->items,$offset,$length,$preserveKeys));}//ArrayAccesspublicfunctionoffsetExists($offset){returnarray_key_exists($offset,$this->items);}publicfunctionoffsetGet($offset){return$this->items[$offset];}publicfunctionoffsetSet($offset,$value){if(is_null($offset)){$this->items[]=$value;}else{$this->items[$offset]=$value;}}publicfunctionoffsetUnset($offset){unset($this->items[$offset]);}//Countablepublicfunctioncount(){returncount($this->items);}//IteratorAggregatepublicfunctiongetIterator(){returnnewArrayIterator($this->items);}//JsonSerializablepublicfunctionjsonSerialize(){return$this->toArray();}/***转换当前数据集为JSON字符串*/publicfunctiontoJson($options=JSON_UNESCAPED_UNICODE){returnjson_encode($this->toArray(),$options);}publicfunction__toString(){return$this->toJson();}/***转换成数组*/protectedfunctionconvertToArray($items){if($itemsinstanceofself){return$items->all();}return(array)$items;}}
给出了例子有前后的关系,所以要结合所有例子来看。
Collection 的原理其实都是对 PHP 内置的函数和 SPL 的应用,如果要更加深入,看官方文档也是一个不错的选择。
Collection 既然是个类,那要怎么才能像数组那样便利的操作呢?如$a['key']。
答案是:继承接口类ArrayAccess,并实现该类的几个接口,如下:
abstractpublicbooleanoffsetExists(mixed$offset)//判断key即$offset的数组元素是否存在,相当于isset($a[$offset])abstractpublicmixedoffsetGet(mixed$offset)//数组元素获取,相当于$value=$a[$offset]abstractpublicvoidoffsetSet(mixed$offset,mixed$value)//数组元素设置相当于$a[$offset]=$valueabstractpublicvoidoffsetUnset(mixed$offset)//删除数组元素,相当于unset($a[$offset]);
现在回头看看 Collection 的源码是怎么实现的。
如何使用,来个例子:
usethink;$c=newCollection;$c['a']='helloa';$c['b']='youareb';echo$c['a'].'<br/>';echo$c['b'].'<br/>';foreach($cas$k=>$v){echo"key:$k,val:$v<br/>";}
结果输出:
helloayouarebkey:a,val:helloakey:b,val:youareb
如果对一个对象进行 json 编码的话,其实就是对该对象的 public 属性进行 json 化,那如何定制 json 化的内容和输出呢?
答案是:继承JsonSerializable接口类,并实现类中的接口:
abstractpublicmixedjsonSerialize(void)//定制json化的字符串输出
现在回头看看 Collection 的源码是怎么实现的。
如何使用,来个例子:
echojson_encode($c).'<br/>';
结果输出:
{"a":"helloa","b":"youareb"}
如果对一个对象进行 count() 操作的话,其实就是统计该对象的 public 属性的总数,那如何定制 count() 呢?
答案是:继承Countable接口类,并实现类中的接口:
abstractpublicintcount(void)
现在回头看看 Collection 的源码是怎么实现的。
如何使用,来个例子:
echo'count:'.$c->count().'<br/>';//或者echo'count:'.count($c).'<br/>';
结果输出:
count:2count:2
如何实现迭代器的功能?如可进行 foreach 操作,提供迭代相关的函数等。
答案是:继承接口类IteratorAggregate(聚合式迭代器),并实现类中的接口:
abstractpublicTraversablegetIterator(void)
如何实现该接口,调用生成一个ArrayIterator类,该类可提供迭代器的所有功能。
现在回头看看 Collection 的源码是怎么实现的。
如何使用,来个例子:
$c['c']='notjustc';$iter=$c->getIterator();//获取迭代器//可方便地使用foreach操作foreach($iteras$k=>$v){echo"key:$k,val:$v<br/>";}echo'count:'.$iter->count().'<br/>';//当前数组元素个数$iter->rewind();//数组位置复位echo'current:'.$iter->current().'<br/>';//当前位置数组元素的值
结果输出:
key:a,val:helloakey:b,val:youarebkey:c,val:notjustccount:3current:helloa
功能操作的一般使用内置函数,PHP 提供的内置函数功能已经很强大了,只需要简单地封装成一个类方法即可,函数其实使用不难,忘记怎么使用了去官网溜溜吧。
有些是为了兼容 PHP 低版本,所以还要另外实现一遍,如:
/***返回数组中指定的一列*/publicfunctioncolumn($column_key,$index_key=null)
内置的array_column()需要 PHP5.5及以上版本才支持,而Collection类的应用目标是5.4及以上能使用,所以勉为其难地再实现了一遍。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。