深入浅出 PHP SPL(PHP 标准库)

什么是spl库?

SPL是用于解决典型问题(standard problems)的一组接口与类的集合。
此扩展只能在php 5.0以后使用,从PHP 5.3.0 不再被关闭,会一直有效.成为php内核组件一部份。
SPL提供了一组标准数据结构。

SPL如何使用?

构建此扩展不需要其他扩展。
更详细的情况可参考  http://php.net/manual/zh/spl.datastructures.php

SPL内容?

官网说,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 );
}

部署了Iterator界面的class示例:

/**
* An iterator for native PHP arrays, re-inventing the wheel
*
* Notice the "implements Iterator" - important!
*/
class ArrayReloaded implements Iterator {

   /**
   * A native PHP array to iterate over
   */
 private $array = array();

   /**
   * A switch to keep track of the end of the array
   */
 private $valid = FALSE;

   /**
   * Constructor
   * @param array native PHP array to iterate over
   */
 function __construct($array) {
   $this->array = $array;
 }

   /**
   * Return the array "pointer" to the first element
   * PHP's reset() returns false if the array has no elements
   */
 function rewind(){
   $this->valid = (FALSE !== reset($this->array));
 }

   /**
   * Return the current array element
   */
 function current(){
   return current($this->array);
 }

   /**
   * Return the key of the current array element
   */
 function key(){
   return key($this->array);
 }

   /**
   * Move forward by one
   * PHP's next() returns false if there are no more elements
   */
 function next(){
   $this->valid = (FALSE !== next($this->array));
 }

   /**
   * Is the current element valid?
   */
 function valid(){
   return $this->valid;
 }
}

使用方法如下:

// Create iterator object
$colors = new ArrayReloaded(array ('red','green','blue',));

// Iterate away!
foreach ( $colors as $color ) {
 echo $color."<br>";
}
你也可以在foreach循环中使用key()方法:
foreach ( $colors as $key => $color ) {
 echo "$key: $color<br>";
}
除了foreach循环外,也可以使用while循环
// Reset the iterator - foreach does this automatically
$colors->rewind();

// Loop while valid
while ( $colors->valid() ) {

   echo $colors->key().": ".$colors->current()."
";
   $colors->next();

}

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

部署ArrayAccess界面的实例:

/**
* A class that can be used like an array
*/
class Article implements ArrayAccess {

 public $title;

 public $author;

 public $category;  

 function __construct($title,$author,$category) {
   $this->title = $title;
   $this->author = $author;
   $this->category = $category;
 }

 /**
 * Defined by ArrayAccess interface
 * Set a value given it's key e.g. $A['title'] = 'foo';
 * @param mixed key (string or integer)
 * @param mixed value
 * @return void
 */
 function offsetSet($key, $value) {
   if ( array_key_exists($key,get_object_vars($this)) ) {
     $this->{$key} = $value;
   }
 }

 /**
 * Defined by ArrayAccess interface
 * Return a value given it's key e.g. echo $A['title'];
 * @param mixed key (string or integer)
 * @return mixed value
 */
 function offsetGet($key) {
   if ( array_key_exists($key,get_object_vars($this)) ) {
     return $this->{$key};
   }
 }

 /**
 * Defined by ArrayAccess interface
 * Unset a value by it's key e.g. unset($A['title']);
 * @param mixed key (string or integer)
 * @return void
 */
 function offsetUnset($key) {
   if ( array_key_exists($key,get_object_vars($this)) ) {
     unset($this->{$key});
   }
 }

 /**
 * Defined by ArrayAccess interface
 * Check value exists, given it's key e.g. isset($A['title'])
 * @param mixed key (string or integer)
 * @return boolean
 */
 function offsetExists($offset) {
   return array_key_exists($offset,get_object_vars($this));
 }

}

使用方法如下:

// Create the object
$A = new Article('SPL Rocks','Joe Bloggs', 'PHP');

// Check what it looks like
echo 'Initial State:<div>';
print_r($A);
echo '</div>';

// Change the title using array syntax
$A['title'] = 'SPL _really_ rocks';

// Try setting a non existent property (ignored)
$A['not found'] = 1;

// Unset the author field
unset($A['author']);

// Check what it looks like again
echo 'Final State:<div>';
print_r($A);
echo '</div>';

运行结果如下:

Initial State:

Article Object
(
   [title] => SPL Rocks
   [author] => Joe Bloggs
   [category] => PHP
)

Final State:

Article Object
(
   [title] => SPL _really_ rocks
   [category] => PHP
)

IteratorAggregate 聚合式迭代器接口

假设对象A实现了上面的ArrayAccess接口,这时候虽然可以像数组那样操作,却无法使用foreach遍历,除非实现了前面提到的Iterator接口。另一个解决方法是,有时会需要将数据和遍历部分分开,这时就可以实现IteratorAggregate接口。它规定了一个getIterator()方法,返回一个使用Iterator接口的object。

IteratorAggregate extends Traversable {
    /* 获取一个外部迭代器 */
    abstract public Traversable getIterator ( void )
}

部署IteratorAggregate界面的实例:

<?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 )//指定index处节点是否存在
    public mixed offsetGet ( mixed $index )//获取指定index处节点值
    public void offsetSet ( mixed $index , mixed $newval )//设置指定index处值
    public void offsetUnset ( mixed $index )//删除指定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 )//检查双链表是否还有节点
}

接下来是使用方法:

$list = new SplDoublyLinkedList();
$list->push('a');
$list->push('b');
$list->push('c');
$list->push('d');
 
$list->unshift('top');
$list->shift();
 
$list->rewind();//rewind操作用于把节点指针指向Bottom所在的节点
echo 'curren node:'.$list->current()."<br />";//获取当前节点
 
$list->next();//指针指向下一个节点
echo 'next node:'.$list->current()."<br />";
 
$list->next();
$list->next();
$list->prev();//指针指向上一个节点
echo 'next node:'.$list->current()."<br />";
 
if($list->current())
    echo 'current node is valid<br />';
else
    echo 'current node is invalid<br />';
     
if($list->valid())//如果当前节点是有效节点,valid返回true
    echo "valid list<br />";
else
  echo "invalid list <br />";
 
var_dump(array(
    'pop' => $list->pop(),
    'count' => $list->count(),    
    'isEmpty' => $list->isEmpty(),    
    'bottom' => $list->bottom(),    
    'top' => $list->top()
));
 
$list->setIteratorMode(SplDoublyLinkedList::IT_MODE_FIFO);
var_dump($list->getIteratorMode());
 
for($list->rewind(); $list->valid(); $list->next()){
    echo $list->current().PHP_EOL;
}
 
var_dump($a = $list->serialize());
//print_r($list->unserialize($a));
 
$list->offsetSet(0,'new one');
$list->offsetUnset(0);
var_dump(array(
    'offsetExists' => $list->offsetExists(4),    
    'offsetGet' => $list->offsetGet(0),
));
var_dump($list);
 
//堆栈,先进后出
$stack = new SplStack();//继承自SplDoublyLinkedList类
 
$stack->push("a<br />");
$stack->push("b<br />");
 
echo $stack->pop();
echo $stack->pop();
echo $stack->offsetSet(0,'B');//堆栈的offset=0是Top所在的位置,offset=1是Top位置节点靠近bottom位置的相邻节点,以此类推
 
$stack->rewind();//双向链表的rewind和堆栈的rewind相反,堆栈的rewind使得当前指针指向Top所在的位置,而双向链表调用之后指向bottom所在位置
echo 'current:'.$stack->current().'<br />';
$stack->next();//堆栈的next操作使指针指向靠近bottom位置的下一个节点,而双向链表是靠近top的下一个节点
echo 'current:'.$stack->current().'<br />';
echo '<br /><br />';
 
//队列,先进先出
$queue = new SplQueue();//继承自SplDoublyLinkedList类
$queue->enqueue("a<br />");//插入一个节点到队列里面的Top位置
$queue->enqueue("b<br />");
$queue->offsetSet(0,'A');//堆栈的offset=0是Top所在的位置,offset=1是Top位置节点靠近bottom位置的相邻节点,以此类推
echo $queue->dequeue();
echo $queue->dequeue();
echo "<br /><br />";

注意:

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

当前节点操作:

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() .'--';

示例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

堆(Heap)就是为了实现优先队列而设计的一种数据结构,它是通过构造二叉堆(二叉树的一种)实现。根节点最大的堆叫做最大堆或大根堆(SplMaxHeap),根节点最小的堆叫做最小堆或小根堆(SplMinHeap)。二叉堆还常用于排序(堆排序)

SplMaxHeap 最大堆

最大堆(SplMaxHeap)继承自抽象类SplHeap实现的。最大堆并没有额外的方法。

SplMinHeap 最小堆

最小堆(SplMinxHeap)继承自抽象类SplHeap实现的。最小堆并没有额外的方法。
如下:最小堆(任意节点的优先级不小于它的子节点)

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

使用方法:

//堆
class MySplHeap extends SplHeap{
    //compare()方法用来比较两个元素的大小,绝对他们在堆中的位置
    public function compare( $value1, $value2 ) {
        return ( $value1 - $value2 );
        }
}
 
$obj = new MySplHeap();
$obj->insert(0);
$obj->insert(1);
$obj->insert(2);
$obj->insert(3);
$obj->insert(4);
echo $obj->top();//4
echo $obj->count();//5
 
foreach ($obj as $item) {
    echo $item."<br />";
}

阵列

优先队列也是非常实用的一种数据结构,可以通过加权对值进行排序,由于排序在php内部实现,业务代码中将精简不少而且更高效。通过SplPriorityQueue::setExtractFlags(int $flag)设置提取方式可以提取数据(等同最大堆)、优先级、和两者都提取的方式。

  • SplFixedArray
SplFixedArray implements Iterator , ArrayAccess , Countable {
  /* 方法 */  
  public __construct ([ int $size = 0 ] )
  public int count ( void )
  public mixed current ( void )
  public static SplFixedArray fromArray ( array $array [, bool $save_indexes = true ] )
  public int getSize ( void )
  public int key ( void )
  public void next ( void )
  public bool offsetExists ( int $index )
  public mixed offsetGet ( int $index )
  public void offsetSet ( int $index , mixed $newval )
  public void offsetUnset ( int $index )
  public void rewind ( void )
  public int setSize ( int $size )
  public array toArray ( void )
  public bool valid ( void )
  public void __wakeup ( void )
}

使用方法:

$arr = new SplFixedArray(4);
$arr[0] = 'php';
$arr[1] = 1;
$arr[3] = 'python';//遍历, $arr[2] 为null
foreach($arr as $v) {
    echo $v . PHP_EOL;
}
 
//获取数组长度
echo $arr->getSize(); //4
 
//增加数组长度
$arr->setSize(5);
$arr[4] = 'new one';
 
//捕获异常
try{
    echo $arr[10];
} catch (RuntimeException $e) {
    echo $e->getMessage();
}


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

映射(SplObjectStorage)

SplObjectStorage是用来存储一组对象的,特别是当你需要唯一标识对象的时候。该类实现了Countable ,Iterator ,Serializable ,ArrayAccess四个接口。可实现统计、迭代、序列化、数组式访问等功能。

示例:

SplObjectStorage implements Countable , Iterator , Serializable , ArrayAccess {
  /* 方法 */  
  public void addAll ( SplObjectStorage $storage )
  public void attach ( object $object [, mixed $data = NULL ] )
  public bool contains ( object $object )
  public int count ( void )
  public object current ( void )
  public void detach ( object $object )
  public string getHash ( object $object )
  public mixed getInfo ( void )
  public int key ( void )
  public void next ( void )
  public bool offsetExists ( object $object )
  public mixed offsetGet ( object $object )
  public void offsetSet ( object $object [, mixed $data = NULL ] )
  public void offsetUnset ( object $object )
  public void removeAll ( SplObjectStorage $storage )
  public void removeAllExcept ( SplObjectStorage $storage )
  public void rewind ( void )
  public string serialize ( void )
  public void setInfo ( mixed $data )
  public void unserialize ( string $serialized )
  public bool valid ( void )
}


使用方法

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

SPL 函数

函数名 描述
class_implements 返回指定的类实现的所有接口
class_parents 返回指定类的父类
class_uses 返回指定类用到的 trait
iterator_apply 为迭代器中每个元素调用一个用户自定义函数
iterator_count 计算迭代器中元素的个数
iterator_to_array 将迭代器中的元素拷贝到数组
spl_classes 返回所有可用的 SPL 类
spl_object_hash 返回指定对象的 hash id
spl_object_id 为指定对象生成独一无二的 id, 类似于 spl_object_hash
spl_autoload_functions 返回所有已注册的 __autoload() 函数
spl_autoload_register 注册给定的函数作为 __autoload 的实现
spl_autoload_unregister 注销已注册的 __autoload() 函数
spl_autoload __autoload() 函数的默认实现
spl_autoload_extensions 注册并返回 spl_autoload 函数使用的默认文件扩展名。
spl_autoload_call 尝试调用所有已注册的 __autoload() 函数来装载请求类