一、总结
直接看代码实例,特别方便易懂
二、php spl标准库简介
SPL简介
SPL是用于解决典型问题(standard problems)的一组接口与类的集合。
SPL是Standard PHP Library(PHP标准库)的缩写。
目前在使用中,SPL更多地被看作是一种使object(对象)模仿array(数组)行为的interfaces和classes。
SPL的常用数据结构
什么是数据结构?
- 数据结构是计算机存储、组织数据的方式。是指相互之间存在的一种或多种特定关系的数据元素的集合。
- 解决的是软件开发过程中的数据如何存储的问题
数据结构之双向链表
双向链表简介
- Bottom:节点,第一个节点称Bottom;
- Top:最后添加的链表的节点称Top;
- 链表指针:可以指向任意节点;
- 当前节点:链表指针指向的节点称为当前节点;
- 节点细化成节点名称,和节点数据;
数据结构之SplDoublyLinkedList类
SplDoublyLinkedList类提供了一个双向链表的主要功能
当前节点操作:
- rewind:将链表的当前指针指向第一个元素
- current:链表当前指针,当节点被删除后,会指向空节点
- prev:上一个
- next:下一个
增加节点操作:
- push 在双向链表的结尾处将元素压入
- unshift 前置双链表元素,预备值在双链表的开始
删除节点操作:
- pop 从双向链表的结尾弹出一个节点,不会改变指针位置
- shift从双向链表的开头弹出一个节点,不会改变指针位置
定位操作:
- bottom 返回当前双向链表的第一个节点的值,当前指针不变
- top返回当前双向链表的最后一个节点的值,当前指针不变
特定节点操作:
- offsetExists 理解为key是否存在
- offsetGet将key节点拿出来
- offsetSet把数据刷新
- offsetUnset删除
代码实现双向链表
SplDoublyLinkedList.php
<?php
/**
*SplDoublyLinkedList 类学习
*/
$obj = new SplDoublyLinkedList();
$obj -> push(1);//把新的节点添加到链表的顶部top
$obj -> push(2);
$obj -> push(3);
$obj -> unshift(10);//把新节点添加到链表底部bottom
print_r($obj);
$obj ->rewind();//rewind操作用于把节点指针指向Bottom所在节点
$obj -> prev();//使指针指向上一个节点,靠近Bottom方向
echo 'next node :'.$obj->current().PHP_EOL;
$obj -> next();
$obj -> next();
echo 'next node :'.$obj->current().PHP_EOL;
$obj -> next();
if($obj -> current())
echo 'current node valid'.PHP_EOL;
else
echo 'current node invalid'.PHP_EOL;
$obj ->rewind();
//如果当前节点是有效节点,valid返回true
if($obj->valid())
echo 'valid list'.PHP_EOL;
else
echo 'invalid list'.PHP_EOL;
print_r($obj);
echo 'pop value :'.$obj -> pop().PHP_EOL;
print_r($obj);
echo 'next node :'.$obj ->current().PHP_EOL;
$obj ->next();//1
$obj ->next();//2
$obj -> pop();//把top位置的节点从链表中删除,并返回,如果current正好指>向top位置,那么调用pop之后current()会失效
echo 'next node:'.$obj -> current().PHP_EOL;
print_r($obj);
$obj ->shift();//把bottom位置的节点从链表中删除,并返回
print_r($obj);
数据结构之堆栈
堆栈简介
先进后出 first in last out
继承自SplDoublyLinkedList类的SplStack类
操作:
- push压入堆
- pop弹出堆栈
-
代码示例
<?php
$stack = new SplStack();
$stack -> push('a');
$stack -> push('b');
$stack -> push('c');//入栈
print_r($stack);
echo 'bottom:'.$stack -> bottom().PHP_EOL;
echo "top:".$stack->top().PHP_EOL;
//堆栈的offset=0,是TOP所在位置(即栈的末尾),offset=1是top位置节点靠近bottom位置的相连节点
$stack -> offsetSet(0,'C');
print_r($stack);
//双向链表的rewind和堆栈的rewind,相反,堆栈的rewind使得当前指针指向top所在位置,而双向链表调用之后指向bottom所在位置
$stack -> rewind();
echo 'current:'.$stack->current().PHP_EOL;
$stack ->next();//堆栈的next操作使指针指向靠近bottom位置的下一个节点,而双向链表是靠近top的下一个节点
echo 'current:'.$stack ->current().PHP_EOL;
//遍历堆栈
$stack -> rewind();
while ($stack->valid()) {
echo $stack->key().'=>'.$stack->current().PHP_EOL;
$stack->next();//不从链表中删除元素
}
//删除堆栈数据
$popObj = $stack -> pop();//弹出最后一个元素,并删除该节点
echo 'pop object:'.$popObj.PHP_EOL;
print_r($stack);
数据结构之队列
- 队列和堆栈刚好相反,最先进入队列的元素会最新走出队列
- 继承自SplDoublyLinkedList类的SplQueue类
-
代码示例
<?php
$obj = new SplQueue();
$obj -> enqueue('a');
$obj -> enqueue('b');
$obj -> enqueue('c');
print_r($obj);
echo 'bottom:'.$obj -> bottom().PHP_EOL;
echo 'top:'.$obj -> top().PHP_EOL;
//队列里的offset=0是指向bottom位置,offset=1是top方向相连的节点
$obj -> offsetSet(0,'A');
valid()
print_r($obj);
//队列里的rewind使得指针指向bottom所在位置的节点
$obj -> rewind();
echo 'current:'.$obj->current().PHP_EOL;
while ($obj ->valid()) {
echo $obj ->key().'=>'.$obj->current().PHP_EOL;
$obj->next();//
}
//dequeue操作从队列中提取bottom位置的节点,并返回,同时从队列里面删除该元素
echo 'dequeue obj:'.$obj->dequeue().PHP_EOL;
print_r($obj);
SPL的常用迭代器
迭代器概述
ArrayIterator
- 熟悉使用seek()跳过元素
- 熟悉使用asort,ksort排序
<?php
$fruits = array(
'apple'=>'apple value',
'orange' => 'orange value',
'grape' => 'grape value',
'pear' => 'pear value'
);
print_r($fruits);//打印数组
echo '*******user fruits directly'.PHP_EOL;
foreach ($fruits as $key => $value) {
# code...
echo $key.':'.$value.PHP_EOL;
}
//使用ArrayIterator遍历数组
$obj = new ArrayObject($fruits);
$it = $obj -> getIterator();//获得迭代器
echo '*******user ArrayIterator in for'.PHP_EOL;
foreach ($it as $key => $value) {
# code...
echo $key.':'.$value.PHP_EOL;
}
echo '*******user ArrayIterator in while'.PHP_EOL;
$it -> rewind();//调用current之前一定要先调用rewind
while ($it -> valid()) {
echo $it -> key().':'.$it->current().PHP_EOL;
$it -> next();//这个必须加上,要不然死循环
}
//跳过某些元素进行打印
echo '*******user seek before while'.PHP_EOL;
$it ->rewind();
if($it->valid()){
$it->seek(1);//跳过第一个元素
while ($it -> valid()) {
echo $it -> key().':'.$it->current().PHP_EOL;
$it -> next();//这个必须加上,要不然死循环
}
}
echo '*******user ksort'.PHP_EOL;
$it->ksort();//对key字典排序
foreach ($it as $key => $value) {
# code...
echo $key.':'.$value.PHP_EOL;
}
echo '*******user asort'.PHP_EOL;
$it->asort();//对value字典排序
foreach ($it as $key => $value) {
# code...
echo $key.':'.$value.PHP_EOL;
}
AppendIterator
能陆续遍历几个迭代器
- -按顺序迭代几个不同的迭代器,例如,希望在一次循环中迭代访问两个或多个组合
<?php
$array_a = new ArrayIterator(array('a','b','c','d'));
$array_b = new ArrayIterator(array('e','f','e','g'));
$it = new AppendIterator();
$it ->Append($array_a);
//通过append方法把迭代器对象添加到AppendIterator对象中
$it -> append($array_b);
foreach ($it as $key => $value) {
# code...
echo $value.PHP_EOL;
}
MultipleIterator
一个迭代器,依次遍历所有附加的迭代器
- 用于把多个Iterator里面的数据组合成一个整体来访问
<?php
ini_set('display_errors', '1');
error_reporting(E_ALL);
$id_iter = new ArrayIterator(array('01','02','03'));
$name_iter = new ArrayIterator(array('张三','李四','王五'));
$age_iter = new ArrayIterator(array('21','22','23'));
$mit = new MultipleIterator (MultipleIterator::MIT_KEYS_ASSOC);
$mit -> attachIterator($id_iter,'ID');
$mit -> attachIterator($name_iter,'name');
$mit -> attachIterator($age_iter,'age');
foreach ($mit as $key => $value) {
# code...
print_r($value);
}
FilesystemIterator
遍历文件系统
<?php
//显示当前目录下的所有文件、大小、创建时间
$it = new FileSystemIterator('.');
date_default_timezone_set('PRC');
foreach ($it as $key => $file_info){
# code...
printf('%s\t%s\t%8s\t%s\n',
date('Y-m-d H:i:s',$file_info->getMTime()),
$file_info->isDir() ? "<dir>" : '',
number_format($file_info->getsize()),
$file_info->getFileName()
);
}
SPL基础接口
接口简介
- 理解Countable、OuterIterator、RecursiveIterator和SeekableIterator接口的概念
- 掌握Countable接口的使用
SPL的基础接口里面定义了最常用的接口 - Countable继承了该接口的类可以直接调用count()得到元素的个数
- OuterIterator如果想对迭代器进行一定的处理之后再返回,可以用这个接口
- RecursiveIterator可以对多层结构的迭代器进行迭代,比如遍历一棵树
- SeekableIterator可以通过seek方法定位到集合里面的某个特定的元素
Countable接口
类实现 Countable 可被用于 count() 函数.
<?php
//Example One, BAD :(
class CountMe
{
protected $_myCount = 3;
public function count()
{
return $this->_myCount;
}
}
$countable = new CountMe();
echo count($countable); //result is "1", not as expected
echo $countable->count;//result is "3"
//Example Two, GOOD :)
class CountMe implements Countable
{
protected $_myCount = 3;
public function count()
{
return $this->_myCount;
}
}
$countable = new CountMe();
echo $countable->count();//result is "3"
echo count($countable); //result is "3" as expected
?>
OuterIterator接口
- OuterIterator如果想对迭代器进行一定的处理之后再返回,可以用这个接口
- IteratorIterator类是OuterIterator的实现,扩展的时候可以直接继承IteratorIterator
<?php
/**
*
*/
class outerImpl extends IteratorIterator
{
public function current()
{
# code...
return parent::current().'_tail';
}
public function key()
{
return 'pre_'.parent::key();
}
}
$array = ['value1','value2','value3','value4'];
$outerObj = new outerImpl(new ArrayIterator($array));
foreach ($outerObj as $key => $value) {
# code...
echo '++'.$key.'-'.$value.PHP_EOL;
}
RecursiveIterator接口
- 可以对多层结构的迭代器进行迭代,比如遍历一棵树
- 所有具有层次结构特点的数据都可以用这个接口遍历,如文件夹
- 关键方法
- hasChildren方法可以用于判断当前节点是否存在子节点
- getChildren方法用于得到当前节点子节点的迭代器
- SPL中实现该接口的类
- RecursiveArrayIterator,RecuriveCachingIterator等以Recurive开头的类都可以进行多层次结构化遍历
SeekableIterator接口
- SeekableIterator
- 可以通过seek方法定位到集合里面的某个特定的元素
- seek方法的参数是元素的位置,从0开始计算
- SPL中实现该接口的类
- ArrayIterator、DirectoryIterator、FilesystemIterator、GlobIterator、RecursiveArrayIterator、RecursiveDirectoryIterator
SPL函数的使用
使用spl_autoload_register装载类
<?php
/**
* libs/Test.class.php
*/
class Test
{
function __construct()
{
# code...
echo 'loading class libs/Test.class.php\n';
}
}
/**
* autoload.php
*/
//设置autoload寻找php定义的类文件的扩展名,多个扩展名用逗号分隔,前面的扩展名优先匹配
spl_autoload_extensions('.class.php,.php');
//设置autoload寻找PHP定义的类文件的目录,多个目录用PATH_SEPARATOR进行分隔
set_include_path(get_include_path().PATH_SEPARATOR.'libs/');
//提示PHP使用autoload机制查找类定义
spl_autoload_register();
new Test();
__autoload装载类
<?php
function __autoload($class_name){
//定义autoload函数,可以在不调用spl_autoload_register函数的情况下完成类的装载
echo '__autoload class :'.$class_name.PHP_EOL;
require_once 'libs/'.$class_name.'.php';//装载类
}
//定义一个替换__autoload函数的类文件装载函数
function classLoader($class_name){
echo 'classloader() load class : '.$class_name.PHP_EOL;
require_once 'libs/'.$class_name.'.php';//装载类
}
//传入定义好的装载类的函数的名称替换__autoload函数
spl_autoload_register('classLoader');
new Test();
自定义__autoload函数装载类
<?php
//定义一个替换__autoload函数的类文件装载函数
function classLoader($class_name){
echo 'classloader() load class : '.$class_name.PHP_EOL;
//当我们不用require_once或require载入类文件的时候,而想通过系统查找include_path来装载类时,必须显式调用spl_autoload函数,参数是类的名称来重启类文件的自动查找(装载)
set_include_path('libs/');
spl_autoload($class_name);
}
//传入定义好的装载类的函数的名称替换__autoload函数
spl_autoload_register('classLoader');
new Test();
SPL函数调用过程
SPL其他函数
SPL的文件处理类
<?php
date_default_timezone_set('PRC');
$file = new SplFileInfo('tmp.txt');
echo 'file is created at '.date('Y-m-d H:i:s',$file->getCTime()).PHP_EOL;
echo 'file is modifyed at '.date('Y-m-d H:i:s',$file->getMTime()).PHP_EOL;
echo 'file size is '.$file->getSize().'bytes'.PHP_EOL;
//读取文件里的内容
$fileObj = $file -> openFile('r');
while ($fileObj ->valid()) {
# code...
echo $fileObj->fgets();
}
//销毁对象
$fileObj = null;
$file = null;