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

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

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

    Spl基本框架

    spl 基本框架.png

典型问题 Commom Problem 比如

  • 数学建模/数据结构

    • 解决数据怎么存储的问题
  • 元素遍历

    • 数据怎么查看的问题
  • 常用方法的统一调用

    • 通用方法(数组、集合的大小)
    • 自定义遍历
  • 类定义在自动装载

    • 让php程序适应大型项目的管理要求,把功能的实现分散到不同文件中

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
                                         )
      
                             )
       */

    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
                                  )
                           )
      
       */

    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
                                  )
      
                  )
       */

    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---
       */

    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---
       */

    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
                  )
       */

    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()
              );
      }

    SPL基础接口 -- 基本描述

  • SPL的基础接口里面定义了最常用的接口

    • Countable

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

      • 如果想对选代器进行一定的处理之后再返回,可以用这个接口
    • Recursivelterator

      • 可以对多层结构的选代器进行迭代,比如遍历- ~棵树
    • Seekablelterator

      • 可以通过seek方法定位到集合里面的某个特定元素

        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);

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
       */

    SPL基础接口 -- RecursiveIterator

  • Recursivelterator

    • 可以对多层结构的选代器进行迭代,比如遍历- -棵树
    • 所有具有层次结构特点的数据都可以用这个接口遍历

      • 如:文件夹
  • 关键方法

    • hasChildren方法用于判断当前节点是否存在子节点
    • getChildren方法用于得到当前节 点子节点的迭代器
  • SPL中实现该接口的类

    • RecursiveArrayIterator , RecursiveCachingIterator等以Recursive开头的类都能够进行多层次结构化的遍历

SPL基础接口 -- Seekablelterator

  • Seekablelterator

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

SPL基础接口 --总结

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

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
       */

    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:July 2, 2022
如果觉得我的文章对你有用,请随意赞赏