前几天有人在群里提到链表之类的,我对其概念也比较模糊,所以就抽空了解了一下,如有理解性或者错误,请谅解并联系我修改,谢谢。

1,什么是SPL?(来自官方说明)

 -spl,指SPL - Standard PHP Library 标准PHP类库。
 -SPL是用于解决典型问题(standard problems)的一组接口与类的集合。

Spl基本框架
spl 基本框架.png

2,典型问题 Commom Problem 比如

-数学建模/数据结构
      -解决数据怎么存储的问题
-元素遍历
      -数据怎么查看的问题
-常用方法的统一调用
      -通用方法(数组、集合的大小)
      -自定义遍历
-类定义在自动装载
      -让php程序适应大型项目的管理要求,把功能的实现分散到不同文件中

3,Spl的常用数据结构 -- 双向链表
如图(简单的画了一下,辅助理解而已。)
双向链表.png

-SplDoublyLinkedList类
   -操作
       -当前节点操作:rewind、current、next、prev
       -增加节点操作:push、unshift
       -删除节点操作:pop、shift
       -定位节点:bottom、top
       -特定节点操作:offsetExists、offsetGet、offsetSet、offsetUnset
   -说明
       -通过代码来学习和理解SplDoublyLinkedList
           -rewind使链表的当前指针指向链表的底部 (头部)
           -push向链表的顶部(尾部)插入-个节点
           -pop :获取链表中的顶部(尾部)节点,并且从链表中删除这个节点;操作不改变当前指针的位置
           -current指向链表当前节点的指针,必须在调用之:前先调用rewind。当指向的节点被删除之后,会指向-个空节点。
           -next让链表当前节点的指针指向下一个节点, current的返回值随之改变
           -unshift向链表的底部(头部)插入-个节点
           -shift删除一 -个链表底部(头部)节点
           -bottom: 获得链表底部(头部)元素,当前指针位置不变
           -top :获得链表顶部(尾部)元素,当前指针位置不变

代码实例:

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 10:08
 */

//实例化双向列表对象
$obj  = new SplDoublyLinkedList();
//添加一个元素
$obj->push(1);
$obj->push(2);
$obj->push(3);
//把新的节点数据添加到链表底部(Bottom)
$obj->unshift(9);
//打印
echo "<pre>";
print_r($obj);
echo "\n";
//rewind操作用于把节点指针指向Bottom所在的节点
$obj->rewind();
//获取系欸但指针指向的节点
echo "current: {$obj->current()}\n";
//下一个节点
$obj->next();
echo "next node: {$obj->current()}\n";
//上一个节点
$obj->prev();
echo "prev node: {$obj->current()}\n";
//注意:当指针只想最后一个节点,再次调用next()返回无效节点,可以做一个判断
if($obj->current()) {//有效节点
    echo "Current node valid\n";
} else {//无效节点
    echo "Current node invalid\n";
}
//valid()判断是否还有元素
if ($obj->valid()) {
    echo "valid list\n";
} else {
    echo "invalid list\n";
}
//删除元素
echo "Pop value:". $obj->pop()."\n";
//打印
echo "<pre>";
print_r($obj);
//把指针指向最后一个元素
$obj->next();
$obj->next();
echo "next node: {$obj->current()}\n";
//删除最后元素,查看指针所在元素无效
$obj->pop();
echo "next node: {$obj->current()}\n";
//打印
echo "<pre>";
print_r($obj);
//把Bottom位置的节点从链表中删除
$obj->shift();
//打印
echo "<pre>";
print_r($obj);
/**
    result:

    SplDoublyLinkedList Object
                     (
                           [flags:SplDoublyLinkedList:private] => 0
                           [dllist:SplDoublyLinkedList:private] => Array
                                   (
                                          [0] => 9
                                          [1] => 1
                                          [2] => 2
                                          [3] => 3
                                   )

                      )
    current: 9
    next node: 1
    prev node: 9
    Current node valid
    valid list
    Pop value:3

    SplDoublyLinkedList Object
                          (
                                [flags:SplDoublyLinkedList:private] => 0
                                [dllist:SplDoublyLinkedList:private] => Array
                                     (
                                                [0] => 9
                                                [1] => 1
                                                [2] => 2
                                       )

                           )
    next node: 2
    next node:
    SplDoublyLinkedList Object
                       (
                             [flags:SplDoublyLinkedList:private] => 0
                             [dllist:SplDoublyLinkedList:private] => Array
                                  (
                                            [0] => 9
                                            [1] => 1
                                   )

                       )
    SplDoublyLinkedList Object
                       (
                             [flags:SplDoublyLinkedList:private] => 0
                             [dllist:SplDoublyLinkedList:private] => Array
                                  (
                                            [1] => 1
                                   )

                       )
 */

4,Spl的常用数据结构 -- 堆栈

   -特点
       -最后进入到堆栈里面的数据最先拿出来(先进后出 FILO:First In Last Out)
   -继承自SplDoublyLinkedList类的SplStack类
   -操作
       - push :压入堆栈 (存入)
       - pop :退出堆栈 (取入)

如图:
堆栈.png
代码实例:

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 10:58
 */

//创建一个堆栈对象
$stack = new SplStack();
$stack->push('a');
$stack->push('b');
$stack->push('c');
//打印
echo "<pre>";
print_r($stack);
echo "Bottom:{$stack->bottom()}\n";
echo "Top:{$stack->top()}\n";
//设置下标为0的值设置为C,堆栈的offset=0是top所在的位置
$stack->offsetSet(0,'C');
echo "<pre>";
print_r($stack);
//双向链表的rewind和堆栈的rewind相反,堆栈的rewind使得当前指向Top所在的位置,而双向链表调用之后指向bottom所在位置。
$stack->rewind();
//获取系欸但指针指向的节点
echo "current: {$stack->current()}\n";
//堆栈的next操作使指针向靠近Bottom位置的下一个节点,而双向链表是靠近Top的下一个节点
$stack->next();
echo "current: {$stack->current()}\n";
//堆栈遍历
$stack->rewind();
while ($stack->valid()) {
    echo "{$stack->key()}=>{$stack->current()}\n";
    $stack->next();
}
//删除堆栈数据
echo "Poped object :{$stack->pop()}\n";
echo "<pre>";
print_r($stack);

/**
    result:

    SplStack Object
                    (
                        [flags:SplDoublyLinkedList:private] => 6
                        [dllist:SplDoublyLinkedList:private] => Array
                            (
                                [0] => a
                                [1] => b
                                [2] => c
                            )

                    )

    Bottom:a
    Top:c

    SplStack Object
                    (
                        [flags:SplDoublyLinkedList:private] => 6
                        [dllist:SplDoublyLinkedList:private] => Array
                            (
                                [0] => a
                                [1] => b
                                [2] => C
                            )
                    )
    current: C
    current: b

    2=>C
    1=>b
    0=>a

    Poped object :C

    SplStack Object
                    (
                        [flags:SplDoublyLinkedList:private] => 6
                        [dllist:SplDoublyLinkedList:private] => Array
                            (
                                [0] => a
                                [1] => b
                            )
                     )

 */

5,SPL的常用数据结构--队列

  -队列和堆栈刚好相反,最先进入队列的元素会最先走出队列
  -就像排队打饭,排在最前面的人总是最先能够打到饭
  -继承自SplDoublyLinkedList类的SplQueue类
  -操作
       -enqueue :进入队列
       -dequeue :退出队列

代码实例:

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 11:33
 */
$obj = new SplQueue();
$obj->enqueue('a');
$obj->enqueue('b');
$obj->enqueue('c');
//打印
echo "<pre>";
print_r($obj);
echo "Bottom:{$obj->bottom()}\n";
echo "Top:{$obj->top()}\n";
//设置下标为0的值设置为A,队列的offset=0是bottom所在的位置
$obj->offsetSet(0,'A');
echo "<pre>";
print_r($obj);
//队列遍历
$obj->rewind();
while ($obj->valid()) {
    echo "{$obj->key()}=>{$obj->current()}\n";
    $obj->next();
}
//删除
echo "dequeue obj:{$obj->dequeue()}\n";
echo "<pre>";
print_r($obj);
/**
result:

SplQueue Object
            (
                    [flags:SplDoublyLinkedList:private] => 4
                    [dllist:SplDoublyLinkedList:private] => Array
                            (
                                    [0] => a
                                    [1] => b
                                    [2] => c
                            )
            )
Bottom:a
Top:c
SplQueue Object
            (
                    [flags:SplDoublyLinkedList:private] => 4
                    [dllist:SplDoublyLinkedList:private] => Array
                            (
                                    [0] => A
                                    [1] => b
                                    [2] => c
                            )
            )

0=>A
1=>b
2=>c
dequeue obj:A
SplQueue Object
            (
                    [flags:SplDoublyLinkedList:private] => 4
                    [dllist:SplDoublyLinkedList:private] => Array
                            (
                                    [0] => b
                                    [1] => c
                            )

            )
 */

6,SPL的常用迭代器--ArrayIterator

-ArrayIterator迭代器用于遍历数组
        -熟悉使用foreach和while语句通过ArrayIterator遍历数组的方法
        -熟悉使用seek跳过某些元素的方法
        -熟悉使用ArrayIterator进行排序的方法

代码实例:

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 11:46
 */

$Arr = [
    'a' => 'c',//position = 1
    'o' => 'v',//position = 2
    'g' => 'w',//position = 3
    'p' => 'b',//position = 4
];
//使用ArrayIterator遍历数组
$obj = new ArrayObject($Arr);
$it = $obj->getIterator();
//foreach 循环
foreach ($it as $key => $value) {
    echo "{$key}:{$value}---";
}
echo "<hr>";
//while循环
$it->rewind();
while ($it->valid()) {
    echo "{$it->key()}:{$it->current()}---";
    $it->next();
}
echo "<hr>";
//跳过某个元素
$it->rewind();
if ($it->valid()) {//避免seek崩溃
    //position = 1 跳过
    $it->seek(1);
    while ($it->valid()) {
        echo "{$it->key()}:{$it->current()}---";
        $it->next();
    }
}
echo "<hr>";
//对key字典排序
$it->ksort();
//foreach 循环
foreach ($it as $key => $value) {
    echo "{$key}:{$value}---";
}
echo "<hr>";
//对value字典排序
$it->asort();
//foreach 循环
foreach ($it as $key => $value) {
    echo "{$key}:{$value}---";
}
echo "<hr>";
/**
 result:
        a:c---o:v---g:w---p:b---
        a:c---o:v---g:w---p:b---
        o:v---g:w---p:b---
        a:c---g:w---o:v---p:b---
        p:b---a:c---o:v---g:w---
 */

7,SPL的常用迭代器--AppendIterator :

-AppendIterator能陆续遍历几个迭代器
        -按顺序迭代访问几个不同的迭代器。例如,希望在-次循环中迭代访问两个或者更多的组合。

代码实例:

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 12:23
 */

//创建两个对象
$Arr1 = new ArrayIterator(['a','b','c']);
$Arr2 = new ArrayIterator(['d','e','f']);

$it = new AppendIterator();
//通过append方法把迭代器对象添加到AppendIterator对象中
$it->append($Arr1);
$it->append($Arr2);

//foreach 循环
foreach ($it as $key => $value) {
    echo "{$value}---";
}
echo "<hr>";
/**
    result:
    a---b---c---d---e---f---
 */

8,SPL的常用选代器-- Multiplelterator

  -Multiplelterator用于把多个Iterator里面的数据组合成为
       -一个整体来访问

代码实例:

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 12:30
 */
header("Content-type:text/html;charset=utf-8;");
//e.g. 编号:01 姓名:张三 年龄:22
$idIter = new ArrayIterator(['01','02','03']);
$nameIter = new ArrayIterator(['张三','李四','王五']);
$ageIter = new ArrayIterator(['22','34','35']);

//MultipleIterator::MIT_KEYS_ASSOC 根据key去关联
$mit = new MultipleIterator(MultipleIterator::MIT_KEYS_ASSOC);

$mit->attachIterator($idIter,'ID');
$mit->attachIterator($nameIter,'NAME');
$mit->attachIterator($ageIter,'AGE');

//foreach 循环
foreach ($mit as $value) {
    echo "<pre>";
    print_r($value);
}
/**
result:
        Array
            (
                [ID] => 01
                [NAME] => 张三
                [AGE] => 22
            )
        Array
            (
                [ID] => 02
                [NAME] => 李四
                [AGE] => 34
            )
        Array
            (
                [ID] => 03
                [NAME] => 王五
                [AGE] => 35
            )
 */

9,SPL的常用迭代器--Filesystemterator

-FilesystemIterator能遍万文件系統

代码实例:

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 12:47
 */

//date_default_timezone_set('PRC');

$it = new FilesystemIterator('.');

foreach ($it as $finfo) {
    echo "<hr>";
    printf(
        "%s\t%s\t%8s\t%s\n",
        date('Y-m-d H:i:s',$finfo->getMTime()),
        $finfo->isDir() ? "<DIR>" : "",
        number_format($finfo->getSize()),
        $finfo->getFileName()
        );
}

10,SPL基础接口--基本描述

-SPL的基础接口里面定义了最常用的接口
   -Countable
       -继承了该接口的类可以直接调用count()得到元素个数
   -OuterIterator
       -如果想对选代器进行一定的处理之后再返回,可以用这个接口
   -Recursivelterator
        -可以对多层结构的选代器进行迭代,比如遍历- ~棵树
   -Seekablelterator
        -可以通过seek方法定位到集合里面的某个特定元素

11,SPL基础接口--Countable

 -Countable
       -继承了该接口的类可以直接调用count()得到元素个数

代码实例:

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 13:40
 */
class countMe implements Countable
{
    protected $my_count = 3;
    public function count()
    {
        return $this->my_count;
    }
}

$obj = new countMe();
echo count($obj);

12,SPL基础接口--Outeriterator

 -Outerlterator
   -如果想对迭代器进行一定的处理之后再返回,可以用这个接口
   -IteratorIterator类是OuterIterator的实现 ,扩展的时候可以直接继承Iteratorlterator

代码实例:

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 13:47
 */
class OuterIt extends IteratorIterator
{
    public function current()
    {
        return parent::current().'_tall';
    }
    public function key()
    {
        return 'Pre_'.parent::key();
    }
}
$Arr = ['v1','v2','v3','v4'];
$outerObj = new OuterIt(new ArrayIterator($Arr));
foreach ($outerObj as $key => $value) {
    echo "++{$key}--{$value}\n",'<hr>';
}

/**
    result:
        ++Pre_0--v1_tall
        ++Pre_1--v2_tall
        ++Pre_2--v3_tall
        ++Pre_3--v4_tall
 */

13,SPL基础接口--RecursiveIterator

-Recursivelterator
   -可以对多层结构的选代器进行迭代,比如遍历- -棵树
   -所有具有层次结构特点的数据都可以用这个接口遍历
        -如:文件夹
   -关键方法
        -hasChildren方法用于判断当前节点是否存在子节点
        -getChildren方法用于得到当前节 点子节点的迭代器
   -SPL中实现该接口的类
         -RecursiveArrayIterator , RecursiveCachingIterator等以Recursive开头的类都能够进行多层次结构化的遍历

14,SPL基础接口--Seekablelterator

-Seekablelterator
      -可以通过seek方法定位到集合里面的某个特定元素
      -seek方法的参数是元素的位置,从0开始计算
   -SPL中实现该接口的类
        ArrayIterator、
        DirectoryIterator、
        FilesystemIterator,
        GlobIterator、
        RecursiveArrayIterator、
        RecursiveDirectoryIterator

15,SPL基础接口--总结

Countable          可以直接应用count()方法获得对象的数目
OuterIterator      可以对迭代器迭代的过程进行自定义
Recursivelterator  可以迭代遍历拥有分层结构的数据
SeekableIterator   可以定位到某个位置的节点

16,SPL函数的使用--Autoload
-什么是Autoload?

   -为了初始化PHP中的类对象,需要通过一定的方法寻找到类的定义。通常情况下,类会定义在一个单独的文件中。

-Autoload就是php找到这些类文件的方法
下面我们通过3个简单的例子去辅助了解一下。
看例子之前,我们先看一下文件的目录结构:
目录结构.png

假设libs目录下时我们要自动加载的类文件
Test.php

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 14:10
 */
class Test
{
   public function __construct()
   {
        echo "Loading Class libs/Test.php\n";
   }
}

Test.class.php

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 14:10
 */
class Test
{
   public function __construct()
   {
        echo "Loading Class libs/Test.class.php\n";
   }
}

代码实例1:Autoload.php

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 14:17
 */

//方法1:

//设置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();
/**
 * result:
 *     Loading Class libs/Test.class.php
 */

代码实例2:Autoload1.php

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 14:31
 */

/**
 * 方法2:
 * 定义__autoload函数可以在不调用spl_autoload_register函数的情况下完后曾类的装载
 * @param $class_name
 */
function __autoload($class_name)
{
    echo "__autoload class:".$class_name."\n";
    require_once ("libs/".$class_name.".php");//装载类
}
//new Test();
/**
 * result:
 *   __autoload class:Test Loading Class libs/Test.php
 */


/**
 * 自定义方法名字
 * @param $class_name
 * 定义一个替换_autoload函数的类文件装载函数
 */
function classLoader($class_name)
{
    echo "classLoader class:".$class_name."\n";
    require_once ("libs/".$class_name.".php");//装载类
}
//传入定义好的装载类的函数的名称替换__autoload函数
spl_autoload_register('classLoader');

new Test();
/**
 * result:
 *   classLoader class:Test Loading Class libs/Test.php
 */

代码实例3:Autoload2.php

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 14:31
 */

/**
 * 自定义方法名字
 * @param $class_name
 * 定义一个替换_autoload函数的类文件装载函数
 */
function classLoader($class_name)
{
    echo "classLoader class:".$class_name."\n";
    //设置Autoload寻找php定义的类文件的目录,多个目录用PATH_SEPARATOR进行分隔
    set_include_path("libs/");
    //当我们不用require载入类文件的时候,而想通过系统查找include_path来装载类时,必须显式调用spl_autoload函数,参数是类的名称来重启类文件的自动查找(装载)
    spl_autoload($class_name);
}
//传入定义好的装载类的函数的名称替换__autoload函数
spl_autoload_register('classLoader');

new Test();
/**
 * result:
 *   classLoader class:Test Loading Class libs/Test.php
 */

17,SPL的文件处理类库

 -SplFileInfo用于获得文件的基本信息,比如修改时间、大小、目录等信息
 -SplFileObject用于操作文件的内容,比如读取、写入

代码实例:

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/7
 * Time: 14:55
 */
$file = new SplFileInfo('1.txt');
echo "File is created at".date('Y-m-d H:i:s',$file->getCTime())."<hr>";
echo "File is modified at".date('Y-m-d H:i:s',$file->getMTime())."<hr>";
echo "File size is created at".$file->getSize()."<hr>";
//读取文件内容
$fileObj = $file->openFile("r");
while ($fileObj->valid()) {
    echo $fileObj->fgets(),'<hr>';//获取文件里面的一行数据
}
$fileObj = null;
$file = null;
/**
 * result:
 *      File is created at2019-08-07 14:55:34
 *      File is created at2019-08-07 14:55:34
 *      File is modified at2019-08-07 14:57:28
 *      File size is created at12
 *      112123321312
 *      12111111111
 *      asasaassaas
 *
 */
Last modification:August 7th, 2019 at 04:33 pm
如果觉得我的文章对你有用,请随意赞赏