<?php ****************************************PHP高级程序员必修课*******************************************************
//SPL:PHP标准库,Standand PHP Library 解决常见问题的一组接口与类的集合
/*
问题:数学建模/数据结构,解决数据如何存储的问题
元素便利,数据如何查看的问题
常用方法的统一调用(通用方法,自定义遍历)
类定义的自动装载
*/
/*
SPL的基本框架
数据结构 基础接口 基础函数
迭代器 异常 其它
*/
/*SPL的常用数据结构
双向链表 堆栈 队列 堆 降序堆 升序堆 优先级队列 定长数组 对象容器
*/
//双向链表
微博用户A <--关注--> 微博用户B <--关注--> 微博用户C //如果是单向的,则为单向链表
注:最先添加到列表当中的节点:Bottom/head
最后添加到列表当中的节点:Top,也称为尾部
链表指针:是当前关注的节点的标识,可以指向任意节点
当前节点:链表指针指向的节点
节点的组成:节点名称key/offset 节点数据value
SqlDoublyLinkedList类
操作:
当前节点操作:rewind(第一个节点),current(当前节点),next(下一个节点),prev(前一个节点)
增加节点操作:push,array_unshift
删除节点操作:pop,shift
定位操作: bottom,top
特定节点操作:offsetExists,offsetGet,offsetSet,offsetUnset
双向链表在SPL中的实现:PHP -f SqlDoublyLinkedList.php可以在cmd中测试
$obj=new SqlDoublyLinkedList();
$obj->push(1); //将1推入链表,也就是添加到顶部top
$obj->push(2);
$obj->push(3);
$obj->unshift(10); //反向将10推入链表,也就是10将键值0占据,也就是添加到底部botton
$obj->rewind(); //调用rewind后,才会有指针,也就是才有current(当前指针的节点值),将指针指向bottom
$obj->next(); //指针向后移动一次
$obj->prev(); //指针向前移动一次
echo "current:".$obj->current(); //获取当前节点的值
if($obj->current){ //判断是否移动完毕
echo "current node valid\n";
}else{
echo "current node invalid\n";
}
if($obj->valid()){ //可以直接用内置函数判断是不是指针移动完毕(判断节点是否有效)
echo "valid list \n";
}
$obj->pop(); //删除离top最近的节点
$obj->shift(); //删除bottom所在位置的节点
双向链表总结:两头皆可操作
//堆栈 算是双向链表的衍生
3号圆盘 |
5号圆盘 |
7号圆盘 |
SqlStack类继承自SqlDoublyLinkedList类
操作:
push:压入堆栈(存入)
pop: 退出堆栈(取出)
代码实现:
$Stack = new SqlStack();
$stack->push('a'); //加入a,b,c
$stack->push('b');
$stack->push('c');
print_r($stack);
echo $stack->bottom;
echo $stack->top;
echo $stack->offsetSet(0,'C'); //堆栈的offset=0是top所在的位置,offset=1是靠近top所在的节点,而在双向链表中相反!!!
//将offset=0的键值对应的value设为C
$stack->rewind();
$stack->current(); //rewind()之后,堆栈指向top位置,双向链表指向底部bottom
$stack->next(); //会指向靠近bottom的方向
//遍历堆栈
$stck->rewind();
while($stck->valid()){
echo $stack->key()."=>".$stack->current()."\n";
$stack->next();
}
//删除堆栈数据
$popobj = $stack->pop();
echo "被删除的元素".$popobj;
堆栈总结:只能从top操作,很多函数如next,prev指向的方向与双向链表相反
//队列:排队打饭一样,先到先打,与堆栈恰恰相反
SqlQueue类也继承自SqlDoublyLinkedList类
enqueue:进入队列
dequeue:退出队列
代码实现:
$obj = new SqlQueue();
$obj->enqueue('a'); //将abc加入到队列
$obj->enqueue('b');
$obj->enqueue('c');
echo "bottom: ".$obj->bottom."\n";
echo "top: ".$obj->top."\n";
$obj->offsetSet(0,'A'); //队列里面0为bottom
$obj->rewind(); //队列里面指向bottom
遍历队列
while($obj->valid()){
echo %$obj->key()."=>".$obj->current()."\n";
$obj->next();
}
//删除
$delobj=>$obj->dequeue();
队列小结:一切以先来先出为原则。
/***************************************************************************************************************************
数据结构大总结:
双向链表,堆栈,队列的关系:
堆栈:先进后出
队列:先进先出
双向链表:两头都可以进出
****************************************************************************************************************************/
//******************************************SPL的常用迭代器************************************************************
迭代器概念:通过某种统一的方式遍历链表或者数组的元素的过程叫做迭代遍历,而这种统一的便利工具我们叫做迭代器。
PHP中的迭代器是通过Iterator接口定义的
常用的迭代器一:ArrayIterator-------------------------------------------------------------------------(用于遍历数组)
1: freach与while语句通过ArrayIterator遍历数组
2:seek跳过某些元素
3:ArrayIterator()进行排序
代码实现:
$fruits = array(
"apple"=>'apple value',
"orange"=>'orange value',
"grape"=>'grape value',
"plum"=>'plum value',
);
//一般遍历
foreach($fruits as $key=>$value){
echo $key.":".$value."\n";
}
//使用ArrayIterator迭代器遍历数组(相当于普通循环的详细化)
$obj = new ArrayObject($fruits);
$it = $obj->getIterator();
foreach($it as $key=>$value){
echo echo $key.":".$value."\n";
}
//使用while之前需要调用rewind()
$it->rewind();
while($it->valid()){
echo $it->key().":".$it->value()."\n";
$it->next();
}
//跳过某些元素进行打印
$it->rewind();
if($it->valid()){
$it->seek(1); //跳过position=1的元素
while($it->valid()){
echo $it->key().":".$it->current()."\n";
$it->next;
}
}
//排序打印
$it->ksort(); //按照key字母排序
foreach($it as $key=>$value){
echo $key.":".$value."\n";
}
//使用value排序
$it->asort();
foreach($it as $key=>$value){
echo $key.":".$value."\n";
}
常用的迭代器二:AppendIterator----------------------------------------------------------------------(按照顺序迭代访问几个不同的迭代器)
代码实现:
$array_a = new ArrayIterator(array('a','b','c'));
$array_b = new ArrayIterator(array('d','e','f'));
$it = new AppendIterator();
$it->append($array_a);
$it->append($array_b);
foreach($it as $key=>$value){
echo $value."\n";
}
/*a
b
c
d
e
f*/
常用的迭代器三:MultipleIterator----------------------------------------------------------------------(将多个Iterator里面的数据组合成为一个整体来访问)
代码实现:
$idIter = new ArrayIterator(array('01','02','03'));
$nameIter = new ArrayIterator(array('张三','李四','王五'));
$ageIter = new ArrayIterator(array('21','23','25'));
$mit = new MultipleIterator(MultipleIterator::MIT_KEY_ASSOC);
$mit->attachIterator($idIter,"ID");
$mit->attachIterator($nameIter,"NAME");
$mit->attachIterator($ageIter,"AGE");
foreach($mit as $value){
print_r($value);
}
/*
array(
[ID]=>01,
[NAME]=>张三,
[AGE]=>22
)
array(
[ID]=>02,
[NAME]=>李四,
[AGE]=>23
)
array(
[ID]=>03,
[NAME]=>王五,
[AGE]=>25
)
*/
常用的迭代器四:FilesystemIterator----------------------------------------------------------------------(遍历文件系统)
代码实现:
date_default_timezone_get('PRC');
$it = new FilesystemIterator('.'); //.表示当前目录
foreach ($it as $finfo){
printf("%s\t%s%8s\t%s\n",
date("Y-m-d H:i:s",$finfo->getMTime())."\n", //打印出当前文件夹下的所有文件的格式化时间
$finfo->isDir()?<DIR>":"", //判断是否是目录
number_format($finfo->getSize()), //将文件大小格式化,每3位加逗号
$finfo->getFileName()
);
}
//******************************************SPL的基础接口************************************************************
1:理解Countable/OuterIterator/RecursiveIterator/SeekableIterator等4个接口的概念
2:熟练掌握Countable接口的使用
Countable:继承该接口的类可以直接调用count得到元素的个数
OuterIterator:如果想对迭代器进行一定的处理之后再返回,可以使用这个接口
RecursiveIterator:可以对多层结构的迭代器进行迭代,比如遍历一棵树
SeekableIterator:通过seek方法定位到集合里面的某一个特定元素
******************************************Countable接口*****************************************************
count(array('name'=>'Peter','id'=>'2'))
date_default_timezone_get('PRC')
$array=array (
array('name'=>'Jonathon','id'=>'2'),
array('name'=>'SEM','id'=>'2'),
array('name'=>'Tom','id'=>'2')
)
echo count($array); //3 自数组个数
echo count($array[1]); //2 第二个子数字的元素个数
class CountMe implements Countable {
protected $_myCount=3;
public function count(){
return $this->_myCount;
}
}
$obj = new CountMe();
echo count($obj); //不加implements Countable时显示1,加后显示为3!!!!!!!!!!
******************************************OuterIterator接口*****************************************************
如果想对迭代器进行一定的处理之后再返回,可以用这个接口
IteratorIterator类时OuterIterator类的实现,扩展的时候可以直接继承IteratorIterator
代码实现:
date_default_timezone_get('PRC');
$array=['Value1','Vlaue2','Vlaue3','Vlaue4'];
$outerObj = new OuterImpl(new Arrayi($array);
forach($outerObj as $key=>$value){
echo "++".$key."-".$vale."\n"; //$key会显示成Pre_0,$value会显示成Value1_tail
}
class OuterImpl extends IteratorIterator{
public function current(){ //value
return parent::current().'_tail';
}
public function key(){ //key
return "Pre_".parent::key();
}
}
******************************************RecursiveIterator接口*****************************************************
见doc的一张图,粗讲。
******************************************SeekableIterator接口*****************************************************
见doc的一张图,粗讲。
//******************************************SPL函数的使用************************************************************
1:
Autoload:为了初始化PHP中的类对象,需要通过一定的方法寻找类的定义。通常情况下,类会定义在一个单独的文件中。
Aotuload就是PHP找到这些类文件的方法。
class Test{
public function __construct(){
echo "Loading Class libs/Test.php\n";
}
}
<?php
spl_autoload_extensions('.class.php,.php'); //会自动加载Test.class.php,也可以设置为.php,两者也可俱在
set_include_path(get_include_path().PATH_SEPARATOR."libs/"); //将类的位置这只在libs/下
//设置autoload寻找定义的类文件的目录,多个目录用_PATH_SEPARATOR进行分割。
spl_autoload_register(); //PHP使用Autoload机制查找类定义
new Test();
?>
2:
__autoload装载类
function __autoload($class_name){
echo "__autoload class :".$class_name."\n";
require_once("libs/".$class_name.'.php');
}
//也可以自定义一个类似__autoload方法
function classLoader($class_name){
echo "classLoader class :".$class_name."\n";
require_once("libs/".$class_name.'.php');
}
spl_autoload_register('classLoader'); //这样也就将__autoload方法重命名了,将__autoload方法顶替了
new Test();
注:还可以将require_once替换成set_include_path顶替,代码实现如下:
function classLoader($class_name){
echo "classLoader class :".$class_name."\n";
set_include_path("libs/");
spl_autoload($class_name); //当我们不用require或者require_once载入类文件的时候,而是想通过系统查找include_path来装载类时,必须显式调用spl_autoload函数,参数是类的名称来重启类文件的自动查找
}
spl_autoload_register('classLoader');
new Test();
*************************************
总结:类载入的基本流程,参见doc文档一图。
*************************************
//******************************************SPL的文件处理类库************************************************************
两个类库
实现代码:
date_default_timezone_get("PRC");
$file = new SqlFileInfo("tmp.txt");
echo "File is created at".date("Y-m-d H:i:s",$file->getCTime())."\n"; //查看文件的信息
echo "File is modified at".date("Y-m-d H:i:s",$file->getMTime())."\n";
//读取里面的内容
$fileObj = $file->openFile("r"); //以读的方式打开一个文件
while($fileObj->valid()){
echo $fileObj->fgets(); //fget获取文件里面的一行数据
}
$openObj = null; //用于关闭打开的文件
$file null;
这里文件的读入方式是面向对象的,比fopen之类的函数要简洁,提倡使用!
****************************************************************************************************************
课程总结:略
****************************************************************************************************************
?>