前几天有人在群里提到链表之类的,我对其概念也比较模糊,所以就抽空了解了一下,如有理解性或者错误,请谅解并联系我修改,谢谢。
什么是SPL?(来自官方说明)
- SPL,指SPL-Standard PHP Library 标准PHP类库。
SPL是用于解决典型问题(standard problems)的一组接口与类的集合。
Spl基本框架
典型问题 Commom Problem 比如
数学建模/数据结构
- 解决数据怎么存储的问题
元素遍历
- 数据怎么查看的问题
常用方法的统一调用
- 通用方法(数组、集合的大小)
- 自定义遍历
类定义在自动装载
- 让php程序适应大型项目的管理要求,把功能的实现分散到不同文件中
Spl的常用数据结构 -- 双向链表
如图(简单的画了一下,辅助理解而已。)
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 :退出堆栈 (取入)
- 如图
代码实例
<?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个简单的例子去辅助了解一下。看例子之前,我们先看一下文件的目录结构:
- 假设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 * */
Comment here is closed