美文网首页
PHP SPL 笔记(1) —— 简介 与 预定义接口

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

作者: jzaaa | 来源:发表于2019-02-12 10:58 被阅读0次

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

    相关文章

      网友评论

          本文标题:PHP SPL 笔记(1) —— 简介 与 预定义接口

          本文链接:https://www.haomeiwen.com/subject/ozlheqtx.html