PHP SPL 笔记(1) —— 简介 与 预定义接口

2019-02-12  本文已影响0人  jzaaa

最近我开始学习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 无法被单独实现,它必须由IteratorAggregateIterator 接口实现

示例:

// 大多情况下判断变量是否遍历
// (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

上面实现了数组式访问对象,但是并不能遍历这个对象,如果想同样遍历对象的话,可以在此接口上实现此前提到的IteratorIteratorAggregate接口。

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提供的一系列内置类,这些类对应不同情景,极大的减少了编程任务。

上一篇 下一篇

猜你喜欢

热点阅读