簡介
SPL是Standard PHP Library(PHP標准庫)的縮寫。
The Standard PHP Library (SPL) is a collection of interfaces and classes that are meant to solve common problems.
官網說,SPL是用來解決典型問題(common problems)的一組接口與類的集合。
那么,什么是common problems
呢?
- 數據結構
解決數據怎么存儲問題
- 元素遍歷
數據怎么查看
- 常用方法的統一調用
數組、集合大小
自定義遍歷
- 類自動加載
spl_autoload_register
包含哪些內容?
- 數據結構
- 基礎接口
- 基礎函數
- 迭代器
- 異常
- 其它
SPL接口
Iterator 迭代器接口
SPL規定,所有實現了Iterator接口的class,都可以用在foreach Loop中。Iterator接口中包含5個必須實現的方法:
interface Iterator extends Traversable{
//返回當前元素
public mixed current ( void );
//返回當前元素的鍵
public scalar key ( void );
//向前移動到下一個元素
public void next ( void );
//返回到迭代器的第一個元素
public void rewind ( void );
//檢查當前位置是否有效
public boolean valid ( void );
}
ArrayAccess 數組式訪問接口
實現ArrayAccess接口,可以使得object像array那樣操作。ArrayAccess接口包含四個必須實現的方法:
interface ArrayAccess {
//檢查一個偏移位置是否存在
public mixed offsetExists ( mixed $offset );
//獲取一個偏移位置的值
public mixed offsetGet( mixed $offset );
//設置一個偏移位置的值
public mixed offsetSet ( mixed $offset );
//復位一個偏移位置的值
public mixed offsetUnset ( mixed $offset );
}
IteratorAggregate 聚合式迭代器接口
假設對象A實現了上面的ArrayAccess接口,這時候雖然可以像數組那樣操作,卻無法使用foreach遍歷,除非實現了前面提到的Iterator接口。
另一個解決方法是,有時會需要將數據和遍歷部分分開,這時就可以實現IteratorAggregate接口。它規定了一個getIterator()
方法,返回一個使用Iterator接口的object。
IteratorAggregate extends Traversable {
/* 獲取一個外部迭代器 */
abstract public Traversable getIterator ( void )
}
示例:
<?php
class myData implements IteratorAggregate {
public $property1 = "Public property one";
public $property2 = "Public property two";
public $property3 = "Public property three";
public function __construct() {
$this->property4 = "last property";
}
public function getIterator() {
return new ArrayIterator($this);
}
}
$obj = new myData;
foreach($obj as $key => $value) {
var_dump($key, $value);
echo "\n";
}
?>
注意:
雖然都繼承自Traversable
,但這是一個無法在 PHP 腳本中實現的內部引擎接口。我們直接使用IteratorAggregate
或Iterator
接口來代替它。
RecursiveIterator
這個接口用於遍歷多層數據,它繼承了Iterator接口,因而也具有標准的current()、key()、next()、 rewind()和valid()方法。同時,它自己還規定了getChildren()
和hasChildren()
方法。The getChildren()
method must return an object that implements RecursiveIterator。
SeekableIterator
SeekableIterator接口也是Iterator接口的延伸,除了Iterator的5個方法以外,還規定了seek()
方法,參數是元素的位置,返回該元素。如果該位置不存在,則拋出OutOfBoundsException。
Countable
這個接口規定了一個count()
方法,返回結果集的數量。
SPL數據結構
數據結構是計算機存儲、組織數據的方式。
SPL提供了雙向鏈表、堆棧、隊列、堆、降序堆、升序堆、優先級隊列、定長數組、對象容器。
基本概念
Bottom:節點,第一個節點稱Bottom;
Top:最后添加的鏈表的節點稱Top;
當前節點(Current):鏈表指針指向的節點稱為當前節點;
SplDoublyLinkedList 雙向鏈表
SplDoublyLinkedList 實現了Iterator
, ArrayAccess
, Countable
接口。
類摘要
SplDoublyLinkedList implements Iterator , ArrayAccess , Countable {
/* 方法 */
public __construct ( void )
public void add ( mixed $index , mixed $newval )
public mixed bottom ( void )
public int count ( void )
public mixed current ( void )
public int getIteratorMode ( void )
public bool isEmpty ( void )
public mixed key ( void )
public void next ( void )
public bool offsetExists ( mixed $index )
public mixed offsetGet ( mixed $index )
public void offsetSet ( mixed $index , mixed $newval )
public void offsetUnset ( mixed $index )
public mixed pop ( void )
public void prev ( void )
public void push ( mixed $value )
public void rewind ( void )
public string serialize ( void )
public void setIteratorMode ( int $mode )
public mixed shift ( void )
public mixed top ( void )
public void unserialize ( string $serialized )
public void unshift ( mixed $value )
public bool valid ( void )
}
注意:
SplDoublyLinkedList::setIteratorMode
用來設置鏈表模式:
迭代方向:
SplDoublyLinkedList::IT_MODE_LIFO (Stack style)
SplDoublyLinkedList::IT_MODE_FIFO (Queue style)
迭代器行為:
SplDoublyLinkedList::IT_MODE_DELETE (Elements are deleted by the iterator)
SplDoublyLinkedList::IT_MODE_KEEP (Elements are traversed by the iterator)
默認模式: SplDoublyLinkedList::IT_MODE_FIFO | SplDoublyLinkedList::IT_MODE_KEEP
當前節點操作:
rewind:將鏈表的當前指針指向第一個元素
current:鏈表當前指針,當節點被刪除后,會指向空節點
prev:上一個
next:下一個
增加節點操作:
push 在雙向鏈表的結尾處將元素壓入
unshift 前置雙鏈表元素,預備值在雙鏈表的開始
刪除節點操作:
pop 從雙向鏈表的結尾彈出一個節點,不會改變指針位置
shift從雙向鏈表的開頭彈出一個節點,不會改變指針位置
定位操作:
bottom 返回當前雙向鏈表的第一個節點的值,當前指針不變
top返回當前雙向鏈表的最后一個節點的值,當前指針不變
特定節點操作:
offsetExists 理解為key是否存在
offsetGet將key節點拿出來
offsetSet把數據刷新
offsetUnset刪除
示例:SplDoublyLinkedList.php
<?php
/**
*SplDoublyLinkedList 類學習
*/
$obj = new SplDoublyLinkedList();
$obj -> push(1);//把新的節點添加到鏈表的頂部top
$obj -> push(2);
$obj -> push(3);
$obj -> unshift(10);//把新節點添加到鏈表底部bottom
print_r($obj);
$obj ->rewind();//rewind操作用於把節點指針指向Bottom所在節點
$obj -> prev();//使指針指向上一個節點,靠近Bottom方向
echo 'next node :'.$obj->current().PHP_EOL;
$obj -> next();
$obj -> next();
echo 'next node :'.$obj->current().PHP_EOL;
$obj -> next();
if($obj -> current())
echo 'current node valid'.PHP_EOL;
else
echo 'current node invalid'.PHP_EOL;
$obj ->rewind();
//如果當前節點是有效節點,valid返回true
if($obj->valid())
echo 'valid list'.PHP_EOL;
else
echo 'invalid list'.PHP_EOL;
print_r($obj);
echo 'pop value :'.$obj -> pop().PHP_EOL;
print_r($obj);
echo 'next node :'.$obj ->current().PHP_EOL;
$obj ->next();//1
$obj ->next();//2
$obj -> pop();//把top位置的節點從鏈表中刪除,並返回,如果current正好指>向top位置,那么調用pop之后current()會失效
echo 'next node:'.$obj -> current().PHP_EOL;
print_r($obj);
$obj ->shift();//把bottom位置的節點從鏈表中刪除,並返回
print_r($obj);
SplStack 棧
棧(Stack)是一種特殊的線性表,因為它只能在線性表的一端進行插入或刪除元素(即進棧和出棧)。
棧是一種后進先出(LIFO)的數據結構。
SplStack 繼承自 雙向鏈表 SplDoublyLinkedList。
示例:
<?php
$stack = new SplStack();
$stack->push(1);
$stack->push(2);
$stack->push(3);
echo 'bottom:'.$stack -> bottom().PHP_EOL;
echo "top:".$stack->top().PHP_EOL;
//堆棧的offset=0,是top所在位置(即棧的末尾)
$stack -> offsetSet(0, 10);
echo "top:".$stack->top().'<br/>';
//堆棧的rewind和雙向鏈表的rewind相反,堆棧的rewind使得當前指針指向top所在位置,而雙向鏈表調用之后指向bottom所在位置
$stack -> rewind();
echo 'current:'.$stack->current().'<br/>';
$stack ->next();//堆棧的next操作使指針指向靠近bottom位置的下一個節點,而雙向鏈表是靠近top的下一個節點
echo 'current:'.$stack ->current().'<br/>';
//遍歷堆棧
$stack -> rewind();
while ($stack->valid()) {
echo $stack->key().'=>'.$stack->current().PHP_EOL;
$stack->next();//不從鏈表中刪除元素
}
echo '<br/>';
echo $stack->pop() .'--';
echo $stack->pop() .'--';
echo $stack->pop() .'--';
輸出:
bottom:1 top:3 top:10
current:10
current:2
2=>10 1=>2 0=>1
10--2--1--
SplQueue 隊列
隊列是一種先進先出(FIFO)的數據結構。使用隊列時插入在一端進行而刪除在另一端進行。
SplQueue 也是繼承自 雙向鏈表 SplDoublyLinkedList,並有自己的方法:
/* 方法 */
__construct ( void )
mixed dequeue ( void )
void enqueue ( mixed $value )
void setIteratorMode ( int $mode )
示例1:
<?php
$queue = new SplQueue();
$queue->enqueue(1);
$queue->enqueue(2);
echo $queue->dequeue() .'--';
echo $queue->dequeue() .'--';
//1--2--
示例2:
<?php
$obj = new SplQueue();
$obj -> enqueue('a');
$obj -> enqueue('b');
$obj -> enqueue('c');
echo 'bottom:'.$obj -> bottom().PHP_EOL;
echo 'top:'.$obj -> top();
echo '<br/>';
//隊列里的offset=0是指向bottom位置
$obj -> offsetSet(0,'A');
echo 'bottom:'.$obj -> bottom();
echo '<br/>';
//隊列里的rewind使得指針指向bottom所在位置的節點
$obj -> rewind();
echo 'current:'.$obj->current();
echo '<br/>';
while ($obj ->valid()) {
echo $obj ->key().'=>'.$obj->current().PHP_EOL;
$obj->next();//
}
echo '<br/>';
//dequeue操作從隊列中提取bottom位置的節點,並返回,同時從隊列里面刪除該元素
echo 'dequeue obj:'.$obj->dequeue();
echo '<br/>';
echo 'bottom:'.$obj -> bottom().PHP_EOL;
輸出:
bottom:a top:c
bottom:A
current:A
0=>A 1=>b 2=>c
dequeue obj:A
bottom:b
SplHeap 堆
堆(Heap)就是為了實現優先隊列而設計的一種數據結構,它是通過構造二叉堆(二叉樹的一種)實現。
根節點最大的堆叫做最大堆或大根堆,根節點最小的堆叫做最小堆或小根堆。二叉堆還常用於排序(堆排序)。
SplHeap 是一個抽象類,實現了Iterator
, Countable
接口。最大堆(SplMaxHeap)和最小堆(SplMinHeap)就是繼承它實現的。最大堆和最小堆並沒有額外的方法。
如皋要使用SplHeap類,需要實現其抽象方法int compare ( mixed $value1 , mixed $value2 )
。
類摘要:
abstract SplHeap implements Iterator , Countable {
/* 方法 */
public __construct ( void )
abstract protected int compare ( mixed $value1 , mixed $value2 )
public int count ( void )
public mixed current ( void )
public mixed extract ( void )
public void insert ( mixed $value )
public bool isEmpty ( void )
public mixed key ( void )
public void next ( void )
public void recoverFromCorruption ( void )
public void rewind ( void )
public mixed top ( void )
public bool valid ( void )
}
示例:
<?php
class MySimpleHeap extends SplHeap
{
//compare()方法用來比較兩個元素的大小,絕對他們在堆中的位置
public function compare( $value1, $value2 ) {
return ( $value1 - $value2 );
}
}
$obj = new MySimpleHeap();
$obj->insert( 4 );
$obj->insert( 8 );
$obj->insert( 1 );
$obj->insert( 0 );
echo $obj->top(); //8
echo $obj->count(); //4
echo '<br/>';
foreach( $obj as $number ) {
echo $number.PHP_EOL;
}
輸出:
84
8 4 1 0
SplMaxHeap 最大堆
最大堆(SplMaxHeap)繼承自抽象類SplHeap實現的。最大堆並沒有額外的方法。
SplMinHeap 最小堆
最小堆(SplMinxHeap)繼承自抽象類SplHeap實現的。最小堆並沒有額外的方法。
如下:最小堆(任意節點的優先級不小於它的子節點)
示例:
<?php
$obj = new SplMinHeap();
$obj->insert(4);
$obj->insert(8);
//提取
echo $obj->extract(). PHP_EOL;
echo $obj->extract();
//4 8
SplPriorityQueue 優先級隊列
優先級隊列SplPriorityQueue是基於堆實現的。和堆一樣,也有int compare ( mixed $priority1 , mixed $priority2 )
方法。
SplPriorityQueue 實現了Iterator
, Countable
接口。
示例:
$pq = new SplPriorityQueue();
$pq->insert('a', 10);
$pq->insert('b', 1);
$pq->insert('c', 8);
echo $pq->count() .PHP_EOL; //3
echo $pq->current() . PHP_EOL; //a
/**
* 設置元素出隊模式
* SplPriorityQueue::EXTR_DATA 僅提取值
* SplPriorityQueue::EXTR_PRIORITY 僅提取優先級
* SplPriorityQueue::EXTR_BOTH 提取數組包含值和優先級
*/
$pq->setExtractFlags(SplPriorityQueue::EXTR_DATA);
while($pq->valid()) {
print_r($pq->current()); //a c b
$pq->next();
}
SplFixedArray 定長數組
SplFixedArray 實現了Iterator
, ArrayAccess
, Countable
接口。
和普通數組不一樣,定長數組規定了數組的長度。優勢就是比普通的數組處理更快。
<?php
$arr = new SplFixedArray(5);
$arr[0] = 1;
$arr[1] = 2;
$arr[2] = 3;
print_r($arr);
//SplFixedArray Object ( [0] => 1 [1] => 2 [2] => 3 [3] => [4] => )
SplObjectStorage 對象容器
SplObjectStorage是用來存儲一組對象的,特別是當你需要唯一標識對象的時候。該類實現了Countable
,Iterator
,Serializable
,ArrayAccess
四個接口。可實現統計、迭代、序列化、數組式訪問等功能。
示例:
class A {
public $i;
public function __construct($i) {
$this->i = $i;
}
}
$a1 = new A(1);
$a2 = new A(2);
$a3 = new A(3);
$a4 = new A(4);
$container = new SplObjectStorage();
//SplObjectStorage::attach 添加對象到Storage中
$container->attach($a1);
$container->attach($a2);
$container->attach($a3);
//SplObjectStorage::detach 將對象從Storage中移除
$container->detach($a2);
//SplObjectStorage::contains用於檢查對象是否存在Storage中
var_dump($container->contains($a1)); //true
var_dump($container->contains($a4)); //false
//遍歷
$container->rewind();
while($container->valid()) {
var_dump($container->current());
$container->next();
}
SPL類
SPL的內置類
SPL除了定義一系列Interfaces以外,還提供一系列的內置類,它們對應不同的任務,大大簡化了編程。
查看所有的內置類,可以使用下面的代碼:
<?php
// a simple foreach() to traverse the SPL class names
foreach(spl_classes() as $key=>$value)
{
echo $key.' -> '.$value.'<br />';
}
?>
SplFileInfo
PHP SPL中提供了SplFileInfo和SplFileObject兩個類來處理文件操作。
SplFileInfo用來獲取文件詳細信息:
$file = new SplFileInfo('foo-bar.txt');
print_r(array(
'getATime' => $file->getATime(), //最后訪問時間
'getBasename' => $file->getBasename(), //獲取無路徑的basename
'getCTime' => $file->getCTime(), //獲取inode修改時間
'getExtension' => $file->getExtension(), //文件擴展名
'getFilename' => $file->getFilename(), //獲取文件名
'getGroup' => $file->getGroup(), //獲取文件組
'getInode' => $file->getInode(), //獲取文件inode
'getLinkTarget' => $file->getLinkTarget(), //獲取文件鏈接目標文件
'getMTime' => $file->getMTime(), //獲取最后修改時間
'getOwner' => $file->getOwner(), //文件擁有者
'getPath' => $file->getPath(), //不帶文件名的文件路徑
'getPathInfo' => $file->getPathInfo(), //上級路徑的SplFileInfo對象
'getPathname' => $file->getPathname(), //全路徑
'getPerms' => $file->getPerms(), //文件權限
'getRealPath' => $file->getRealPath(), //文件絕對路徑
'getSize' => $file->getSize(),//文件大小,單位字節
'getType' => $file->getType(),//文件類型 file dir link
'isDir' => $file->isDir(), //是否是目錄
'isFile' => $file->isFile(), //是否是文件
'isLink' => $file->isLink(), //是否是快捷鏈接
'isExecutable' => $file->isExecutable(), //是否可執行
'isReadable' => $file->isReadable(), //是否可讀
'isWritable' => $file->isWritable(), //是否可寫
));
SplFileObject
SplFileObject繼承SplFileInfo
並實現RecursiveIterator
、 SeekableIterator
接口 ,用於對文件遍歷、查找、操作遍歷:
try {
foreach(new SplFileObject('foo-bar.txt') as $line) {
echo $line;
}
} catch (Exception $e) {
echo $e->getMessage();
}
查找指定行:
try {
$file = new SplFileObject('foo-bar.txt');
$file->seek(2);
echo $file->current();
} catch (Exception $e) {
echo $e->getMessage();
}
寫入csv文件:
$list = array (
array( 'aaa' , 'bbb' , 'ccc' , 'dddd' ),
array( '123' , '456' , '7891' )
);
$file = new SplFileObject ( 'file.csv' , 'w' );
foreach ( $list as $fields ) {
$file -> fputcsv ( $fields );
}
獲取文件指定行內容
/**
* 獲取文件指定行內容
* @param $file_name
* @param int $start
* @param int $limit
* @return array
*/
public static function file_get_line($file_name, int $start, int $limit)
{
$f = new \SplFileObject($file_name, 'r');
$f->seek($start);
$ret = [];
for ($i = 0; $i < $limit; $i++) {
$val = $f->current();
if ($val) {
$ret[] = trim($val, PHP_EOL); //去除換行符
}
$f->next();
}
return $ret;
}
DirectoryIterator
該類繼承自SplFileInfo
並實現SeekableIterator
接口。
這個類用來查看一個目錄中的所有文件和子目錄:
<?php
try{
/*** class create new DirectoryIterator Object ***/
foreach ( new DirectoryIterator('./') as $Item )
{
echo $Item.'<br />';
}
}
/*** if an exception is thrown, catch it here ***/
catch(Exception $e){
echo 'No files Found!<br />';
}
?>
ArrayObject
該類實現了ArrayAccess
,Countable
, IteratorAggregate
, Serializable
接口。
這個類可以將Array轉化為object。
<?php
/*** a simple array ***/
$array = array('koala', 'kangaroo', 'wombat', 'wallaby', 'emu', 'kiwi', 'kookaburra', 'platypus');
/*** create the array object ***/
$arrayObj = new ArrayObject($array);
/*** iterate over the array ***/
for($iterator = $arrayObj->getIterator();
/*** check if valid ***/
$iterator->valid();
/*** move to the next array member ***/
$iterator->next())
{
/*** output the key and current array value ***/
echo $iterator->key() . ' => ' . $iterator->current() . '<br />';
}
?>
ArrayIterator
該類實現了ArrayAccess
, Countable
, SeekableIterator
, Serializable
接口。
這個類實際上是對ArrayObject類的補充,為后者提供遍歷功能。
<?php
/*** a simple array ***/
$array = array('koala', 'kangaroo', 'wombat', 'wallaby', 'emu', 'kiwi', 'kookaburra', 'platypus');
try {
$object = new ArrayIterator($array);
foreach($object as $key=>$value)
{
echo $key.' => '.$value.'<br />';
}
}
catch (Exception $e)
{
echo $e->getMessage();
}
?>
參考
1、PHP: SPL - Manual
http://php.net/manual/zh/book.spl.php
2、PHP: 預定義接口 - Manual
http://php.net/manual/zh/reserved.interfaces.php
3、PHP SPL筆記 - 阮一峰的網絡日志
http://www.ruanyifeng.com/blog/2008/07/php_spl_notes.html
4、PHP SPL標准庫之文件操作(SplFileInfo和SplFileObject) - PHP點點通
http://www.phpddt.com/php/SplFileObject.html