<?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之类的函数要简洁,提倡使用!








****************************************************************************************************************

课程总结:略

****************************************************************************************************************





?>