PHP SPL 笔记(1) —— 简介 与 预定义接口
最近我开始学习PHP
中的SPL
库,这是PHP 5.3
就添加的东西,但网上相关资料却不是很多。这里零散的记录自己的学习内容,目的是加深自己的印象,文中有错误的地方欢迎指出。
注:文章部分示例需在高版本PHP(5.6+)下运行
[TOC]
SPL简介
SPL是用于解决典型问题(standard problems)的一组接口与类的集合。
SPL(Standard PHP Library),中文翻译过来叫PHP标准库,它在PHP 5.3
以后成为PHP内核组件的一部分,所以我们无需其他依赖就可以使用。
SPL是PHP内置的一组接口与类的集合,通常我们使用它来让Object
拥有Array
的特性。
Iterator (迭代器) 简介
Iterator是SPL的核心,它为不同对象提供了遍历的功能。例如,一个对象,一个文本每一行构成的集合,文件目录的集合,我们都可以利用Iterator实现像Array
一样遍历。另外,在处理大数据量和特殊情况时,Iterator更具有优势(例如yield的使用)。
Interfaces (预定义接口)
在学习SPL类之前,我们先来学习几个预定义接口。
Traversable (遍历) 接口
Traversable 是作为所有可遍历接口的基础接口,目的是为了检测一个类是否可以使用foreach
遍历。
Traversable 无法被单独实现,它必须由IteratorAggregate
或 Iterator
接口实现
示例:
// 大多情况下判断变量是否遍历
// (object)$items 可以被foreach遍历,但是无法通过下面检测
if( !is_array( $items ) && !$items instanceof Traversable ) {
// 无法被遍历
}
注意:array
和(object)array
仍然可以被foreach
遍历,但是他们并没有实现Traversable
接口,所以一般情况下Traversable
并不能作为foreach
兼容性检测
Iterator (迭代器)
Iterator是SPL的核心,实现了外部迭代器可以迭代对象的接口。
interface Iterator extends Traversable {
/**
* 返回当前元素
*/
public function current();
/**
* 向前移动到下一个元素
*/
public function next();
/**
* 返回当前元素的键
*/
public function key();
/**
* 检查当前位置是否有效
*/
public function valid();
/**
* 返回到迭代器的第一个元素
*/
public function rewind();
}
我们简单的实现一个迭代器:
// 可以打印每个方法,来查看何时调用的这个函数
class Test implements Iterator {
private $position;
private $array = [];
public function __construct(array $array) {
$this->position = 0;
$this->array = $array;
}
function rewind() {
// reset($this->array);
$this->position = 0;
}
function current() {
// return current($this->array);
return $this->array[$this->position];
}
function key() {
// return key($this->array);
return $this->position;
}
function next() {
// next($this->array);
++$this->position;
}
function valid() {
// return !is_null(key($this->array));
return isset($this->array[$this->position]);
}
}
使用:
$test = new Test([
'first',
'second',
'third'
]);
// foreach 迭代
foreach ($test as $key => $item) {
var_dump($key . ' => ' . $item);
}
// while 迭代
$test->rewind();
while ($test->valid()) {
var_dump($test->key() . ' => ' . $test->current());
$test->next();
}
IteratorAggregate(聚合式迭代器)
IteratorAggregate 是创建外部迭代器的接口,必须实现getIterator
方法
示例:
// 实现foreach循环输出拼接指定字符串
class Test implements \IteratorAggregate
{
private $items = [];
private $splice;
public function __construct($items = [], $splice = '')
{
$this->items = $items;
$this->splice = $splice;
}
public function getIterator()
{
$return = function() {
while(list($key, $val) = each($this->items)) {
yield $key => $val . $this->splice;
}
};
return $return();
}
}
使用:
$test = new Test([
'first',
'second',
'third'
], 'c');
foreach ($test as $key => $item) {
var_dump($key . ' => ' . $item);
}
// 打印,原数组不会改变
0 => firstc
1 => secondc
2 => thirdc
ArrayAccess(数组式访问)
利用 ArrayAccess 接口,可以让 Object 像 Array 一样操作。该接口需要实现4个方法。
interface ArrayAccess {
/**
* 检查一个偏移位置是否存在
*/
public function offsetExists($offset);
/**
* 获取一个偏移位置的值
*/
public function offsetGet($offset);
/**
* 设置一个偏移位置的值
*/
public function offsetSet($offset, $value);
/**
* 复位一个偏移位置的值
*/
public function offsetUnset($offset);
}
示例:
class Test implements \ArrayAccess
{
private $data = [];
// object 赋值调用
public function __set($name, $value)
{
$this->data[$name] = $value;
}
// object 获取值调用
public function &__get($name)
{
return $this->data[$name];
}
public function __isset($key) {
return isset($this->data[$key]);
}
public function __unset($key) {
unset($this->data[$key]);
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return isset($this->data[$offset]) ? $this->data[$offset] : null;
}
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->data[] = $value;
} else {
$this->data[$offset] = $value;
}
}
public function offsetUnset($offset)
{
if ($this->offsetExists($offset)) {
unset($this->data[$offset]);
}
}
}
使用:
// object可以像array一样获取/设置值
$test = new Test;
$test->name = 'John';
var_dump($test);
$test['name'] = 'Tom';
var_dump($test);
var_dump($test['name']);
var_dump($test->name);
// 打印
Lib\Test Object
(
[data:Lib\Test:private] => Array
(
[name] => John
)
)
Lib\Test Object
(
[data:Lib\Test:private] => Array
(
[name] => Tom
)
)
Tom
Tom
上面实现了数组式访问对象,但是并不能遍历这个对象,如果想同样遍历对象的话,可以在此接口上实现此前提到的Iterator
或IteratorAggregate
接口。
Serializable(序列化)
序列化的接口,此接口必须实现两个方法:
interface Serializable {
/**
* 序列化
*/
public function serialize();
/**
* 反序列化
*/
public function unserialize($serialized);
}
Countable
Countable 接口实现了被count()
函数计数,该接口必须实现1个方法
interface Countable {
/**
* 统计一个对象的元素个数
*/
public function count();
}
RecursiveIterator
RecursiveIterator可用于递归迭代迭代器。也就是说RecursiveIterator用于遍历多层数据,它继承了Iterator,并且规定了hasChildren()
和getChildren()
方法。
interface RecursiveIterator extends Iterator {
/**
* 返回是否可以为当前条目创建迭代器
*/
public function hasChildren();
/**
* 返回当前条目的迭代器
*/
public function getChildren();
}
SeekableIterator
SeekableIterator接口实现通过键值查找元素,它继承了Iterator,并规定了seek()
方法。
interface SeekableIterator extends Iterator {
public function seek($position);
}
示例:
// 在上部分Iterator基础上增加seek方法
class Test implements \SeekableIterator
{
private $position;
private $array = [];
public function __construct(array $array) {
$this->position = 0;
$this->array = $array;
}
function rewind() {
// reset($this->array);
$this->position = 0;
}
function current() {
// return current($this->array);
return $this->array[$this->position];
}
function key() {
// return key($this->array);
return $this->position;
}
function next() {
// next($this->array);
++$this->position;
}
function valid() {
// return !is_null(key($this->array));
return isset($this->array[$this->position]);
}
function seek($position)
{
if (!isset($this->array[$position])) {
throw new \OutOfBoundsException("{$position} seek position 无效");
}
$this->position = $position;
}
}
使用:
$test = new Test([
'first',
'second',
'third'
]);
try {
$test->seek(1);
var_dump($test->current());
$test->seek(3);
var_dump($test->current());
} catch (\OutOfBoundsException $e) {
var_dump($e->getMessage());
}
// 打印
second
3 seek position 无效
小结
至此,了解了大部分SPL所涉及到的基础接口,后面将涉及SPL提供的一系列内置类,这些类对应不同情景,极大的减少了编程任务。