简介:SPL的常用数据结构以及常用迭代器等,主要是参考慕课网的《站在巨人的肩膀上写代码-SPL》教程
1-1 课程概述
1. 课程准备知识:
- 熟悉和了解数据结构的基本概念
- 熟悉PHP代码的编写
- 熟悉面向对象的概念
2. 课程目录:
- 什么是SPL
- SPL的基本框架
- SPL的常用数据结构
- SPL的常用迭代器
- SPL的接口
- SPL函数的使用
- SPL的文件处理类库
3. 什么是SPL
- SPL是Standard PHP Library的缩写
- 官方定义:The Standard PHP Library(SPL) is a collection of interfaces and classes that are meant to solve common problems。它是用于解决典型(常见)问题(common problems)的一组接口与类的集合。
4. Common Problem
- 数据建模/数据结构
- 解决数据怎么存储的问题
- 元素遍历
- 数据怎么查看的问题
- 常用方法的统一调用:
- 通用方法(数组、集合的大小)
- 自定义遍历
- 类定义的自动装载
- 让PHP程序适应大型项目的管理要求,把功能的实现分散到不同的文件中
5. SPL的基本框架:
2-1 SPL数据结构简介
1. 什么是数据结构:
- 数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。
- 解决的是软件开发过程中的数据如何存储和表示的问题
2. SPL提供的数据结构有:
双向链表、堆栈、队列、堆、降序堆、升序堆、优先级队列、定长数组、对象容器
2-2 SPL数据结构之双向链表简介
1. 单项链表:如图:
2. 双向链表:如图:
3. 基本概念:
- Bottom:最先添加到链表中的节点叫做Bottom(底部),也称为头部(Head),比如说上图中的节点1
- Top:最后添加到链表中的节点叫做Top(顶部),也称为尾部,比如说上图中的节点3
- 链表指针:是一个当前关注的节点的标识,可以指向任意节点
- 当前节点:链表指针指向的节点称为当前节点
4. 每一个节点又可以细分化:
- 节点名称:可以在链表中唯一标识一个节点的名称,通常又称为节点的key或者offset
- 节点数据:存放在链表中的应用数据,通常称为value
比如说:
2-3 SPL数据结构之SplDoublyLinkedList类
1. SplDoublyLinkedList类操作:
- 当前节点操作:
- rewind(使当前节点指向头部)
- current(获得当前节点)
- next(使当前节点到下一个节点)
- prev(使当前节点到上一个节点)
- 增加节点操作:
- push(尾部添加一个节点)
- unshift(头部添加一个节点)
- 删除节点操作
- pop(删除尾部的第一个节点)
- shift(删除头部的第一个节点)
- 定位操作:
- bottom(获取头部的元素)
- top(获取底部的元素)
- 特定节点操作:
- offsetExists(判断特定节点是否存在)
- offsetGet(获得某个节点的数据)
- offsetSet(设置某个节点的数据)
- offsetUnset(释放某个节点的数据)
2-4 SPL数据结构之双向链表的代码实现
1. 完整的代码为:
2_4_SplDoublyLinkedList.php:
<?php
/**
* 双向链表的简单操作
*/
// 实例化
$SplDoubly_obj = new SplDoublyLinkedList();
// 链表的顶部/尾部(Top)添加节点数据
$SplDoubly_obj->push(1);
$SplDoubly_obj->push(2);
$SplDoubly_obj->push(3);
// 底部/头部(Bottom)添加节点数据
$SplDoubly_obj->unshift(10);
echo "输出双向链表:" . PHP_EOL;
var_dump($SplDoubly_obj);
/*
[0] => 10
[1] => 1
[2] => 2
[3] => 3
*/
echo PHP_EOL;
// 判断当前节点是否有效(由于未设置起始节点,所以输出无效)
if ($SplDoubly_obj->valid()) {
echo "当前节点有效" . PHP_EOL;
} else {
echo "当前节点无效" . PHP_EOL;
}
// 获取节点指针指向的节点(当前节点),由于还没有设置起始节点,所以这里输出NULL
echo "当前节点为:" . PHP_EOL;
var_dump($SplDoubly_obj->current());
// rewind操作用于把节点指针指向头部Bottom所在的节点
$SplDoubly_obj->rewind();
echo "设置了起始节点后,当前节点为:" . $SplDoubly_obj->current() . PHP_EOL;
echo "第一个节点的值为:" . $SplDoubly_obj->bottom() . PHP_EOL; // 10
echo "最后一个节点的值为:" . $SplDoubly_obj->top() . PHP_EOL; // 3
// bottom和top只是获取首尾的值而已,并不会定位到该节点
echo "当前节点为:" . $SplDoubly_obj->current() . PHP_EOL; // 10
// 使当前节点指向下一个节点
$SplDoubly_obj->next();
$SplDoubly_obj->next();
echo "指向下一个下一个节点后,当前节点为:" . $SplDoubly_obj->current() . PHP_EOL; // 2
// 判断当前节点是否有效
if ($SplDoubly_obj->valid()) {
echo "当前节点有效" . PHP_EOL;
} else {
echo "当前节点无效" . PHP_EOL;
}
// 使当前节点指向下一个节点
$SplDoubly_obj->next();
$SplDoubly_obj->next();
echo "指向下一个下一个节点后,当前节点为:" . PHP_EOL;
// 由于下一个节点没有数据,所以这里输出NULL
var_dump($SplDoubly_obj->current());
// 判断当前节点是否有效(建议用valid来判断,如果当前节点的数据为0或者是false或者是null的话会被判断为无效)
if ($SplDoubly_obj->current()) {
echo "当前节点有效" . PHP_EOL;
} else {
echo "当前节点无效" . PHP_EOL;
}
// 使当前节点指向上一个节点
$SplDoubly_obj->prev();
$SplDoubly_obj->prev();
echo "指向上一个上一个节点后,当前节点为:" . PHP_EOL;
// 由于当前节点已经是NULL,再指向上一个节点,即使上一个节点是有数据的,也同样显示NULL
var_dump($SplDoubly_obj->current());
echo PHP_EOL;
// 删除并返回最后的节点
$SplDoubly_obj->rewind();
echo "重新指向第一个节点后,当前节点为:" . $SplDoubly_obj->current() . PHP_EOL; // 10
$SplDoubly_obj->next();
$SplDoubly_obj->next();
$SplDoubly_obj->next();
echo "指向下一个下一个下一个节点后,当前节点为:" . $SplDoubly_obj->current() . PHP_EOL; // 3
$pop = $SplDoubly_obj->pop();
echo "删除的最后一个节点为:" . $pop . PHP_EOL; // 3
echo "输出双向链表:" . PHP_EOL;
var_dump($SplDoubly_obj);
/*
[0] => 1
[1] => 2
*/
echo "当前节点为:" . PHP_EOL;
// 如果最后一个节点被删除了然后恰好当前节点正好指在最后一个节点,current会输出null
var_dump($SplDoubly_obj->current());
// 删除并返回第一个节点
$first = $SplDoubly_obj->shift();
echo "删除的第一个节点为:" . $first . PHP_EOL; // 10
var_dump($SplDoubly_obj);
// 设置第一个节点的值
$SplDoubly_obj->offsetSet(0, 'AA');
echo "输出双向链表:" . PHP_EOL;
var_dump($SplDoubly_obj);
/*
[0] => AA
[1] => 2
*/
运行:
输出双向链表:
C:\laragon\www\test\PHP_SPL\2_4_SplDoublyLinkedList.php:18:
class SplDoublyLinkedList#1 (2) {
private $flags =>
int(0)
private $dllist =>
array(4) {
[0] =>
int(10)
[1] =>
int(1)
[2] =>
int(2)
[3] =>
int(3)
}
}
当前节点无效
当前节点为:
C:\laragon\www\test\PHP_SPL\2_4_SplDoublyLinkedList.php:36:
NULL
设置了起始节点后,当前节点为:10
第一个节点的值为:10
最后一个节点的值为:3
当前节点为:10
指向下一个下一个节点后,当前节点为:2
当前节点有效
指向下一个下一个节点后,当前节点为:
C:\laragon\www\test\PHP_SPL\2_4_SplDoublyLinkedList.php:65:
NULL
当前节点无效
指向上一个上一个节点后,当前节点为:
C:\laragon\www\test\PHP_SPL\2_4_SplDoublyLinkedList.php:79:
NULL
重新指向第一个节点后,当前节点为:10
指向下一个下一个下一个节点后,当前节点为:3
删除的最后一个节点为:3
输出双向链表:
C:\laragon\www\test\PHP_SPL\2_4_SplDoublyLinkedList.php:94:
class SplDoublyLinkedList#1 (2) {
private $flags =>
int(0)
private $dllist =>
array(3) {
[0] =>
int(10)
[1] =>
int(1)
[2] =>
int(2)
}
}
当前节点为:
C:\laragon\www\test\PHP_SPL\2_4_SplDoublyLinkedList.php:102:
NULL
删除的第一个节点为:10
C:\laragon\www\test\PHP_SPL\2_4_SplDoublyLinkedList.php:107:
class SplDoublyLinkedList#1 (2) {
private $flags =>
int(0)
private $dllist =>
array(2) {
[0] =>
int(1)
[1] =>
int(2)
}
}
输出双向链表:
C:\laragon\www\test\PHP_SPL\2_4_SplDoublyLinkedList.php:112:
class SplDoublyLinkedList#1 (2) {
private $flags =>
int(0)
private $dllist =>
array(2) {
[0] =>
string(2) "AA"
[1] =>
int(2)
}
}
有一些地方要注意:
- 如果没有设置起始节点的话,当前节点是无效的,即输出current和valid都是null
- bottom和top只是获取首尾的数据而已,并不会把当前指针定位到首尾去
- 通过next和prev来移动当前指针,如果移动到的节点不存在的话,当前节点是无效的,此时再通过prev或者是next返回上一个位置的话也同样是输出当前节点是无效的
- 如果通过pop或者是shift来删除节点,刚好当前节点也是在被删除的节点的时候,那么当前节点是无效的
- 输出当前节点的时候,最好是先判断一下当前节点是否有效
2-5 SPL数据结构之双向链表小结
1. 常用的方法:
2-6 SPL数据结构之堆栈的简介
1. 堆栈:最后进入到堆栈里的数据最先拿出堆栈,即First In Last Out(FILO)
如图:
2. PHP的堆栈类:继承自SplDoublyLinkenList类的SplStack类
操作:
- push:压入堆栈(存入)
- pop:退出堆栈(取出)
2-7 SPL数据结构之堆栈的代码实现
1. 完整的代码:
2_7_SplStack.php:
<?php
/**
* 堆栈的简单操作
* push:压入堆栈(存入)
* pop:退出堆栈(取出)
*
* 注意:堆栈里面的方法有一些和双向链表的相反,比如说next和prev,rewind了之后的第一个节点是最后一个节点。还有堆栈中的第一个节点是最后一个节点
*/
// 实例化
$stack = new SplStack();
// 添加节点数据
$stack->push('a');
$stack->push('b');
$stack->push('c');
echo "输出堆栈:" . PHP_EOL;
print_r($stack);
/*
[0] => a
[1] => b
[2] => c
*/
echo PHP_EOL;
echo "第一个节点的值为:" . $stack->bottom() . PHP_EOL;
echo "最后一个节点的值为:" . $stack->top() . PHP_EOL;
// 设置最后一个节点。注意:和双向链表相反,这里的0代表的是最后一个节点而不是第一个节点
$stack->offsetSet(0, 'AA');
echo PHP_EOL . "输出堆栈:" . PHP_EOL;
print_r($stack);
/*
[0] => a
[1] => b
[2] => AA
*/
// 注意:和双向链表相反,这里的rewind之后当前节点是最后一个节点
$stack->rewind();
echo "设置了起始节点后,当前节点为:" . $stack->current() . PHP_EOL;
// 注意:next和prev也是和双向链表的相反
$stack->next();
echo "下一个节点后,当前节点为:" . $stack->current() . PHP_EOL;
// 遍历堆栈
echo "遍历的节点为:" . PHP_EOL;
$stack->rewind();
while ($stack->valid()) {
echo $stack->key() . "=>" . $stack->current() . PHP_EOL;
$stack->next();
}
// 删除并返回堆栈最后一个节点
$pop = $stack->pop();
echo "删除的最后一个节点为:" . $pop . PHP_EOL;
echo PHP_EOL . "输出堆栈:" . PHP_EOL;
print_r($stack);
/*
[0] => a
[1] => b
*/
运行:
输出堆栈:
SplStack Object
(
[flags:SplDoublyLinkedList:private] => 6
[dllist:SplDoublyLinkedList:private] => Array
(
[0] => a
[1] => b
[2] => c
)
)
第一个节点的值为:a
最后一个节点的值为:c
输出堆栈:
SplStack Object
(
[flags:SplDoublyLinkedList:private] => 6
[dllist:SplDoublyLinkedList:private] => Array
(
[0] => a
[1] => b
[2] => AA
)
)
设置了起始节点后,当前节点为:AA
下一个节点后,当前节点为:b
遍历的节点为:
2=>AA
1=>b
0=>a
删除的最后一个节点为:AA
输出堆栈:
SplStack Object
(
[flags:SplDoublyLinkedList:private] => 6
[dllist:SplDoublyLinkedList:private] => Array
(
[0] => a
[1] => b
)
)
注意:堆栈里面的方法有一些和双向链表的相反,比如说next和prev,rewind了之后的第一个节点是最后一个节点。还有堆栈中的第一个节点是最后一个节点
2-8 SPL数据结构之队列
1. 队列正好和堆栈相反,最先进入队列的元素会最先走出队列
2. 队列继承自SplDoublyLinkedList类的SplQueue类
操作:
- enqueue:进入队列
- dequeue:退出队列
3. 队列的操作和堆栈非常的类似
代码:
2_8_SplQueue.php:
<?php
/**
* 队列的简单操作
*
* 队列正好和堆栈相反,最先进入队列的元素会最先走出队列
*
* 操作:
* enqueue:进入队列
* dequeue:退出队列
* 注意:队列和堆栈的操作方法基本上是类似的,不过有一些方法是和堆栈相反的,比如说offsetSet里面的0,表示的是第一个节点,而不像堆栈表示的是最后一个节点。还有rewind、next、prev操作
*/
// 实例化
$queue = new SplQueue();
// 添加节点数据
$queue->enqueue('a');
$queue->enqueue('b');
$queue->enqueue('c');
echo "输出队列:" . PHP_EOL;
print_r($queue);
/*
[0] => a
[1] => b
[2] => c
*/
echo PHP_EOL . "第一个节点的值为:" . $queue->bottom() . PHP_EOL; // a
echo "最后一个节点的值为:" . $queue->top() . PHP_EOL; // c
// 设置第一个节点。注意:和双向链表一致,这里的0代表的是第一个节点,但是和堆栈是相反的,堆栈的0表示的是最后一个节点
$queue->offsetSet(0, 'AA');
echo "输出队列:" . PHP_EOL;
print_r($queue);
/*
[0] => AA
[1] => b
[2] => c
*/
// 注意:和双向链表一致,这里的rewind之后当前节点是第一个节点,和堆栈是相反的
$queue->rewind();
echo PHP_EOL . "设置了起始节点后,当前节点为:" . $queue->current() . PHP_EOL; // AA
// 注意:next和prev也是和双向链表的一致,和堆栈的相反
$queue->next();
echo "下一个节点后,当前节点为:" . $queue->current() . PHP_EOL; // b
// 遍历队列
echo "遍历的节点为:" . PHP_EOL;
$queue->rewind();
while ($queue->valid()) {
echo $queue->key() . "=>" . $queue->current() . PHP_EOL;
$queue->next();
}
// 删除并返回队列的第一个节点
$pop = $queue->dequeue();
echo "删除的第一个节点为:" . $pop . PHP_EOL; // AA
echo "输出队列:" . PHP_EOL;
print_r($queue);
/*
[0] => b
[1] => c
*/
运行:
输出队列:
SplQueue Object
(
[flags:SplDoublyLinkedList:private] => 4
[dllist:SplDoublyLinkedList:private] => Array
(
[0] => a
[1] => b
[2] => c
)
)
第一个节点的值为:a
最后一个节点的值为:c
输出队列:
SplQueue Object
(
[flags:SplDoublyLinkedList:private] => 4
[dllist:SplDoublyLinkedList:private] => Array
(
[0] => AA
[1] => b
[2] => c
)
)
设置了起始节点后,当前节点为:AA
下一个节点后,当前节点为:b
遍历的节点为:
0=>AA
1=>b
2=>c
删除的第一个节点为:AA
输出队列:
SplQueue Object
(
[flags:SplDoublyLinkedList:private] => 4
[dllist:SplDoublyLinkedList:private] => Array
(
[0] => b
[1] => c
)
)
注意:队列和堆栈的操作方法基本上是类似的,不过有一些方法是和堆栈相反的,比如说offsetSet里面的0,表示的是第一个节点,而不像堆栈表示的是最后一个节点。还有rewind、next、prev操作
3-1 SPL迭代器概述
1. 什么是迭代器:通过某种统一的方式遍历链表或者数组中的元素的过程叫做遍历,而这种统一的遍历工具叫做迭代器
2. PHP中的迭代器是通过Iterator接口去定义的,如图是Iterator的操作方法:
3-2 Arraylterator迭代器
1. ArrayInterator迭代器:用于遍历数组
2. 如果要来遍历数组的话,传统的方法是用foreach即可,即:
<?php
$fruits = [
'apple' => 'apple value',
'orange' => 'orange value',
'grape' => 'grape value',
'plum' => 'plum value'
];
// 使用传统的foreach遍历数组
echo "使用传统的foreach遍历数组:" . PHP_EOL;
foreach ($fruits as $key => $value) {
echo $key . " : " . $value . PHP_EOL;
}
也可以用 ArrayInterator迭代器来遍历数组:
<?php
$fruits = [
'apple' => 'apple value',
'orange' => 'orange value',
'grape' => 'grape value',
'plum' => 'plum value'
];
// 实例化ArrayIterator
$obj = new ArrayObject($fruits);
$it = $obj->getIterator();
// 使用ArrayIterator来遍历数组(foreach)
echo PHP_EOL . "使用ArrayIterator来遍历数组(foreach):" . PHP_EOL;
foreach ($it as $key => $value) {
echo $key . " : " . $value . PHP_EOL;
}
// 使用ArrayIterator来遍历数组(while)
echo PHP_EOL . "使用ArrayIterator来遍历数组(while):" . PHP_EOL;
$it->rewind();
while ($it->valid()) {
echo $it->key() . " : " . $it->current() . PHP_EOL;
$it->next();
}
3. 如果是要取部分数组的话,用 ArrayInterator迭代器的话更方便一些:
<?php
// 跳过某些元素进行打印
echo PHP_EOL . "使用ArrayIterator来遍历数组(跳过某些元素):" . PHP_EOL;
$it->rewind();
if ($it->valid()) {
$it->seek(2); // 设置指针的位置,为n时即跳过前面n-1的元素
while ($it->valid()) {
echo $it->key() . " : " . $it->current() . PHP_EOL;
$it->next();
}
}
/*
grape : grape value
plum : plum value
*/
4. 也可以对数组进行排序打印:
<?php
// 对key进行字典序排序
echo PHP_EOL . "使用ArrayIterator来遍历数组(对key排序):" . PHP_EOL;
$it->ksort();
$it->rewind();
while ($it->valid()) {
echo $it->key() . " : " . $it->current() . PHP_EOL;
$it->next();
}
/*
apple : apple value
grape : grape value
orange : orange value
plum : plum value
*/
// 对value进行字典序排序
echo PHP_EOL . "使用ArrayIterator来遍历数组(对value排序):" . PHP_EOL;
$it->asort();
$it->rewind();
while ($it->valid()) {
echo $it->key() . " : " . $it->current() . PHP_EOL;
$it->next();
}
/*
apple : apple value
grape : grape value
orange : orange value
plum : plum value
*/
完整的代码为:
3_1_ArrayIterator.php:
<?php
/**
* 相比起foreach遍历数组,ArrayIterator在排序和取部分数组上面更有优势
*/
$fruits = [
'apple' => 'apple value',
'orange' => 'orange value',
'grape' => 'grape value',
'plum' => 'plum value'
];
// 使用传统的foreach遍历数组
echo "使用传统的foreach遍历数组:" . PHP_EOL;
foreach ($fruits as $key => $value) {
echo $key . " : " . $value . PHP_EOL;
}
// 实例化ArrayIterator
$obj = new ArrayObject($fruits);
$it = $obj->getIterator();
// 使用ArrayIterator来遍历数组(foreach)
echo PHP_EOL . "使用ArrayIterator来遍历数组(foreach):" . PHP_EOL;
foreach ($it as $key => $value) {
echo $key . " : " . $value . PHP_EOL;
}
// 使用ArrayIterator来遍历数组(while)
echo PHP_EOL . "使用ArrayIterator来遍历数组(while):" . PHP_EOL;
$it->rewind();
while ($it->valid()) {
echo $it->key() . " : " . $it->current() . PHP_EOL;
$it->next();
}
// 跳过某些元素进行打印
echo PHP_EOL . "使用ArrayIterator来遍历数组(跳过某些元素):" . PHP_EOL;
$it->rewind();
if ($it->valid()) {
$it->seek(2); // 设置指针的位置,为n时即跳过前面n-1的元素
while ($it->valid()) {
echo $it->key() . " : " . $it->current() . PHP_EOL;
$it->next();
}
}
/*
grape : grape value
plum : plum value
*/
// 对key进行字典序排序
echo PHP_EOL . "使用ArrayIterator来遍历数组(对key排序):" . PHP_EOL;
$it->ksort();
$it->rewind();
while ($it->valid()) {
echo $it->key() . " : " . $it->current() . PHP_EOL;
$it->next();
}
/*
apple : apple value
grape : grape value
orange : orange value
plum : plum value
*/
// 对value进行字典序排序
echo PHP_EOL . "使用ArrayIterator来遍历数组(对value排序):" . PHP_EOL;
$it->asort();
$it->rewind();
while ($it->valid()) {
echo $it->key() . " : " . $it->current() . PHP_EOL;
$it->next();
}
/*
apple : apple value
grape : grape value
orange : orange value
plum : plum value
*/
输出:
附加:
PHP的简单的数组代码:
<?php
/**
* PHP的数组简单操作
*/
$array = ['step one', 'step two', 'step three', 'step four'];
// 返回当前指针的值
echo current($array) . PHP_EOL; // "step one"
// 指向下一个指针并且返回指针的值
echo next($array) . PHP_EOL; // "step two"
// 指向上一个指针并且返回指针的值
echo prev($array) . PHP_EOL; // "step one"
// 指向下一个指针
next($array); // "step two"
// 指向下一个指针
next($array); // "step three"
// 返回当前指针的值
echo current($array) . PHP_EOL; // "step three"
// 将指针倒回到第一个并返回第一个的值
reset($array);
// 返回当前指针的值
echo current($array) . PHP_EOL; // "step one"
// 将指针倒回到最后一个并返回最后一个的值返回当前指针的值
echo end($array) . PHP_EOL;
// 返回当前指针的值
echo current($array) . PHP_EOL; // "step one"
3-3 Appendlterator迭代器
1. AppendIterator迭代器:陆续遍历几个迭代器
2. AppendIterator迭代器可以通过append方法把多个ArrayIterator迭代器对象放到一起来遍历
代码:
<?php
/**
* AppendIterator迭代器:陆续遍历几个迭代器
*
* AppendIterator迭代器可以通过append方法把多个ArrayIterator迭代器对象放到一起来遍历
*/
$aa = ['a', 'b', 'c'];
$bb = ['d', 'e', 'f'];
// 实例化ArrayIterator对象
$array_a = new ArrayIterator($aa);
$array_b = new ArrayIterator($bb);
// 实例化AppendIterator对象
$it = new AppendIterator();
// 通过append方法把多个迭代器对象添加到AppendIterator对象中
$it->append($array_a);
$it->append($array_b);
foreach ($it as $key => $value) {
echo $key . " : " . $value . PHP_EOL;
}
/*
0 : a
1 : b
2 : c
0 : d
1 : e
2 : f
*/
3-4 Multiplelterator迭代器
1. MultipleIterator迭代器用于把多个Iterator里面的数据组合成为一个整体来访问
2. 完整的代码为:
3_4_MultipleIterator.php:
<?php
// 实例化ArrayIterator对象
$idIter = new ArrayIterator(['01', '02', '03']);
$nameIter = new ArrayIterator(['张三', '李四', '王五']);
$ageIter = new ArrayIterator(['22', '34', '35']);
// 设置容器(以key为关联)
$mit = new MultipleIterator(MultipleIterator::MIT_KEYS_ASSOC);
// 将迭代器加入容器中
$mit->attachIterator($idIter, "0");
$mit->attachIterator($nameIter, "1");
$mit->attachIterator($ageIter, "2");
// 遍历容器
foreach ($mit as $value) {
var_dump($value);
}
运行:
Array
(
[0] => 01
[1] => 张三
[2] => 22
)
Array
(
[0] => 02
[1] => 李四
[2] => 34
)
Array
(
[0] => 03
[1] => 王五
[2] => 35
)
如果将迭代器放入容器改成:
$mit->attachIterator($idIter, "ID");
$mit->attachIterator($nameIter, "NAME");
$mit->attachIterator($ageIter, "AGE");
运行:
Array
(
[ID] => 01
[NAME] => 张三
[AGE] => 22
)
Array
(
[ID] => 02
[NAME] => 李四
[AGE] => 34
)
Array
(
[ID] => 03
[NAME] => 王五
[AGE] => 35
)
3-5 Filesystemlterator迭代器
1. Filesystemlterator迭代器用于遍历文件系统
2. 完整的代码为:
3_5_Filesystemlterator.php:
<?php
/**
* Filesystemlterator迭代器
*/
if (!ini_get('date.timezone')) {
date_default_timezone_set('PRC');
}
$it = new FilesystemIterator(".");
foreach ($it as $value) {
echo $value->isDir() ? "目录名:" : "文件名:";
echo $value->getFileName();
echo " 大小为:" . number_format($value->getSize() / 1000, 2) . " KB";
echo " 创建时间为:" . date("Y-m-d H:i:s", $value->getMTime()) . PHP_EOL;
}
/*
目录名:.git 大小为:4.10 KB 创建时间为:2018-01-11 14:49:39
目录名:.idea 大小为:4.10 KB 创建时间为:2018-01-11 14:49:39
文件名:2_4_SplDoublyLinkedList.php 大小为:5.25 KB 创建时间为:2018-01-10 20:14:43
文件名:2_7_SplStack.php 大小为:1.75 KB 创建时间为:2018-01-10 21:43:06
文件名:2_8_SplQueue.php 大小为:2.00 KB 创建时间为:2018-01-11 11:52:45
文件名:3_2_Array.php 大小为:0.93 KB 创建时间为:2018-01-11 14:07:53
文件名:3_2_ArrayIterator.php 大小为:2.10 KB 创建时间为:2018-01-11 13:56:42
文件名:3_3_Appendlterator.php 大小为:0.67 KB 创建时间为:2018-01-11 14:23:54
文件名:3_4_MultipleIterator_1.php 大小为:0.95 KB 创建时间为:2018-01-11 14:39:39
文件名:3_4_MultipleIterator_2.php 大小为:0.98 KB 创建时间为:2018-01-11 14:40:45
文件名:3_5_Filesystemlterator.php 大小为:0.46 KB 创建时间为:2018-01-11 14:49:38
目录名:img 大小为:0.00 KB 创建时间为:2018-01-11 14:27:50
文件名:README.md 大小为:7.86 KB 创建时间为:2018-01-11 14:48:27
*/
4-1 SPL接口简介
1. SPL的基础接口里面定义了最常用的接口:
- Countable:继承了该接口的类可以直接调用count()得到元素个数
- OuterIterator:如果想对迭代器进行一定的处理之后再返回,可以用这个接口
- RecursiveIterator:可以对多层结构的迭代器进行迭代,比如遍历一棵树
- SeekableIterator:可以通过seek方法定位到集合里面的某个特定元素
4-2 Countable接口
1. count()方法是对象继承Countable后必须实现的方法,即某个类继承Countable,类中必须定义count方法。这样的话,直接使用count方法时会调用对象自身的count方法。
2. 完整的代码:
4_2_Countable.php:
<?php
/**
* Countable接口
* count()方法是对象继承Countable后必须实现的方法,即某个类继承Countable,类中必须定义count方法。这样的话,直接使用count方法时会调用对象自身的count方法
*/
$array = [
['name' => 'paul1', 'id' => 5],
['name' => 'paul2', 'id' => 6],
['name' => 'paul3', 'id' => 7],
];
echo count($array) . PHP_EOL; // 3
echo count($array[1]) . PHP_EOL; // 2
class CountMe1
{
protected $_myCount = 3;
public function count()
{
return $this->_myCount;
}
}
// 类(对象)没有继承Countable接口的话,不会调用对象的count方法
$obj1 = new CountMe1();
echo count($obj1) . PHP_EOL; // 1
class CountMe2 implements Countable
{
protected $_myCount = 3;
public function count()
{
return $this->_myCount;
}
}
// 类(对象)继承Countable接口的话,调用count方法自动会调用对象的count方法,相当于$obj2->count()
$obj2 = new CountMe2();
echo count($obj2) . PHP_EOL; // 等同于echo $obj2->count(); 3
4-3 Outerlterator接口
1. OuterIterator接口:可以对迭代器进行一定的处理后返回
2. IteratorIterator类是OuterIterator接口的实现,扩展的时候可以直接继承
IteratorIterator类。
3. 完整的代码(重写了ArrayIterator迭代器的current和key方法):
4_3_OuterIterator.php:
<?php
/**
* OuterIterator接口:可以对迭代器进行一定的处理后返回
* IteratorIterator类是OuterIterator接口的实现,扩展的时候可以直接继承IteratorIterator类。
*
* 利用OutIterator接口对ArrayIterator迭代器的current和key方法进行内部处理(重写)
*/
$array = ["Value1", "Value2", "Value3", "Value4"];
$ArrayIt = new ArrayIterator($array);
// 正常的遍历
foreach ($ArrayIt as $key => $value) {
echo $key . ":" . $value . PHP_EOL;
}
/*
0:Value1
1:Value2
2:Value3
3:Value4
*/
echo PHP_EOL;
class OutIterator extends IteratorIterator
{
public function current()
{
return parent::current() . "_tail";
}
public function key()
{
return "pre_" . parent::key();
}
}
// 对ArrayIterator迭代器的current和key方法进行内部处理(重写)
$outerObj = new OutIterator($ArrayIt);
foreach ($outerObj as $key => $value) {
echo $key . ":" . $value . PHP_EOL;
}
/*
pre_0:Value1_tail
pre_1:Value2_tail
pre_2:Value3_tail
pre_3:Value4_tail
*/
4-4 Recursivelterator接口
1. Recursivelterator接口:可以对多层结构的迭代器进行迭代,比如说遍历一棵树。所有具有层次结构特点的数据都可以用这个接口遍历,比如说文件夹。
2. 关键方法:
- hasChildren方法:用于判断当前节点是否存在子节点
- getChildren方法:用于得到当前节点子节点的迭代器
3. SPL中实现该接口的类:
RecursiveArrayIterator、RecursiveCachingIterator、
RecursiveDirectory
Iterator
等以Recursive开头的类都能够进行多层次结构化的遍历
4-5 Seekablelterator接口
1. Seekablelterator可以通过seek方法定位到集合里面的某个特定元素
2. seek方法:参数是元素的位置,从0开始计算(在3_2_ArrayIterator.php有使用)
3. SPL中实现该接口的类:
ArrayIterator、DirectoryIterator、Filesystem
Iterator、Glob
Iterator、RecursiveArray
Iterator、RecursiveDirectory
Iterator
5-1 SPL使用spl_autoload_register函数装载类
1. Autoload:为了初始化PHP中的对象,需要通过一定的方法定位到类的定义。通常情况下,类会定义在一个单独的文件中。Autoload就是找到这些类文件的方法,即类自动加载函数
2. 先在当前目录定义一个新的目录,命名为:5_1_Class,里面放入两个php,内容都为:
<?php
class Test
{
public function __construct()
{
echo "加载Test.class.php的Test Class,这是初始化";
}
}
稍微echo的内容不一样,其他的都是一样的,都是Test方法,两个php一个命名为Test.php,另一个是Test.class.php。如图:
然后目录外定义一个PHP,来演示自动装载类:
5_1_autoload_register.php:
<?php
/**
* spl_autoload_register函数
* 类自动加载函数
*/
// 注册并返回spl_autoload函数使用的默认文件扩展名,可以有多个,先找第一个,如没有才找接下来的后缀
spl_autoload_extensions('.php, .class.php');
// 加载类的路径
set_include_path(get_include_path() . PATH_SEPARATOR . "5_1_Class/");
// 让类的自动加载生效
spl_autoload_register();
// 调用自动加载的类
new Test();
// 加载Test.php的Test,这是初始化
如果代码是:
spl_autoload_extensions('.class.php, .php');
那么会输出: 加载Test.class.php的Test Class,这是初始化
5-2 SPL使用__autoload装载类
1. 除了上面的使用spl_autoload_register来实现自动装载类,还可以使用__autoload函数来装载类
5_2_autoload.php:
<?php
/**
* autoload装载类
*/
/**
* 定义__autoload函数,可以自动完成类的装载
* @param $class_name
*/
function __autoload($class_name)
{
echo "__autoload class:" . $class_name . PHP_EOL;
// 装载类
require_once('5_1_Class/' . $class_name . ".php");
}
new Test();
/**
__autoload class:Test
加载Test.php的Test,这是初始化
*/
2. 如果spl_autoload_register函数和__autoload函数同时存在的时候,原来的 __autoload()方法将不会再调用。
比如:
<?php
/**
* 如果spl_autoload_register函数和__autoload函数同时存在的时候,原来的 __autoload()方法将不会再调用
*/
/**
* 定义__autoload函数,可以自动完成类的装载
* @param $class_name
*/
function __autoload($class_name)
{
echo "__autoload class:" . $class_name . PHP_EOL;
// 装载类
require_once('5_1_Class/' . $class_name . ".php");
}
/**
* 定义一个用来替换__autoload函数的类文件装载函数
* 需要使用spl_autoload_register('classLoader')来实现自动装载
* @param $class_name
*/
function classLoader($class_name)
{
echo "classLoader() load class:" . $class_name . PHP_EOL;
// 装载类
require_once('5_1_Class/' . $class_name . ".php");
}
// 传入定义好的类文件装载函数来实现自动装载
spl_autoload_register('classLoader');
new Test();
/*
classLoader() load class:Test
加载Test.php的Test,这是初始化
*/
3. spl_autoload_register自定义自动加载函数,首先定义一个自动加载的函数,然后使用spl_auto_register([自动加载函数名])来调用该函数。
5-3 SPL通过自定义的__autoload函数装载类
1. 上面的例子是通过require_once函数来载入类文件,如果我们不想通过require或者是require_once来载入类文件时,而是想通过系统自动查找文件名来装载类的时候,必须显式调用spl_autoload函数,参数为类的名称来重启类文件的自动查找(装载),另外,当使用 spl_autoload函数的时候,require函数会失去作用了。
代码:
5_3_splautoload.php:
<?php
/**
* spl_autoload装载类
* 系统可以通过显式调用spl_autoload函数自动查找文件名来装载类,参数为类的名称来重启类文件的自动查找(装载)
*
* 注意:当使用 spl_autoload函数的时候,require函数会失去作用了
*/
/**
* 定义一个用来替换__autoload函数的类文件装载函数
* 需要使用spl_autoload_register('classLoader')来实现自动装载
* @param $class_name
*/
function classLoader($class_name)
{
echo "classLoader() load class:" . $class_name . PHP_EOL;
// 装载类
// require_once('5_1_Class/' . $class_name . ".php");
set_include_path('5_1_Class/');
spl_autoload($class_name);
}
// 传入定义好的类文件装载函数来实现自动装载
spl_autoload_register('classLoader');
new Test();
/*
classLoader() load class:Test
加载Test.php的Test,这是初始化
*/
5-4 SPL其他函数
1. SPL类载入基本流程:
2. 迭代器相关函数:
- iterator_apply:为迭代器中每个元素调用一个用户自定义函数(比如说对数组迭代器中每一个元素进行平方等)
- iterator_count:计算迭代器中元素的个数
- iterator_to_array:将迭代器中的元素拷贝到数组
3. 类信息相关函数:
- class_implements:返回指定的类实现的所有接口
- instanceof:判断某个对象是否实现了某个接口或者是某个类的实例
- class_parents:返回指定类的父类,如果继承了多次,会把所有的父类都打印了出来
6-1 SPL的文件处理类库
1. 文件处理类库:
- SplFileInfo:用于获得文件的基本信息,比如修改时间、大小、目录等信息
- SplFileObject:用于操作文件的内容,比如读取、写入
2. 具体代码:
6_1_SplFile.php:
<?php
/**
* 文件处理类库
*
* SplFileInfo:用于获得文件的基本信息,比如修改时间、大小、目录等信息
* SplFileObject:用于操作文件的内容,比如读取、写入
*/
date_default_timezone_set('PRC');
$url = "5_1_Class/Test.php";
$file = new SplFileInfo($url);
echo "文件创建时间:" . date("Y-m-d H:i:s", $file->getCTime()) . PHP_EOL;
echo "文件修改时间:" . date("Y-m-d H:i:s", $file->getMTime()) . PHP_EOL;
echo "文件大小:" . $file->getSize() / 1000 . " KB" . PHP_EOL;
// 读取文件的内容
$fileObj = $file->openFile("r");
while ($fileObj->valid()) {
// fgets函数用于获取文件里面的一行数据
echo $fileObj->fgets();
}
// 销毁变量
$fileObj = null;
$file = null;
/*
运行:
文件创建时间:2018-01-11 16:11:05
文件修改时间:2018-01-11 16:11:17
文件大小:0.131 KB
<?php
class Test
{
public function __construct()
{
echo "加载Test.php的Test,这是初始化";
}
}
*/