A SPL (Standard PHP Library) é uma coleção de interfaces e classes que se destinam a resolver problemas comuns. Ela é composta principalmente de classes, estruturas de dados(filas, pilhas e heap) e iteradores para percorrê-las.

Antes de entrarmos nas estruturas de dados é interessante conhecer as principais interfaces do SPL. Começando pela Iterator.

Interface Iterator

A interface iterator defini cinco métodos para serem utilizados para move através de uma coleção.

currentRetorna o elemento atual
keyRetorna a chave do elemento atual
nextMove para o próximo elemento
rewindVolta o iterator para o primeiro elemento
validVerifica se o primeiro elemento é válido

Podemos implementar esta interface e utilizar um foreach para percorrer os dados de nossa classe pois Iterator extende a interface Traversable o que a permite ser percorrida através de um foreach. Veja um exemplo.

final class Book
{
    public string $name;
    public string $author;
    public function __construct(string $name, string $author)
    {
        $this->name = $name;
        $this->author = $author;
    }
}

final class BooksCollection implements Iterator
{
    private array $books;
    private int $index = -1;

    public function insert(Book $book): void
    {
        $this->books[] = $book;
    }

    public function current(): mixed
    {
        return $this->books[$this->index];
    }
    public function key(): mixed
    {
        return $this->index;

    }
    public function next(): void
    {
        $this->index++;
    }
    public function rewind(): void
    {
        $this->index = 0; 
    }
    public function valid(): bool
    {
        return $this->index >= 0 && $this->index < count($this->books);
    }
}
$books = new BooksCollection();
$books->insert( new Book('Senhor dos Anéis','J. R. R. Tolkien'));
$books->insert( new Book('A Caçada ao Outubro Vermelho','Tom Clancy'));
$books->insert( new Book('A Guerra dos Tronos','George R. R. Martin'));

foreach( $books as $book){
    echo "{$book->name} - {$book->author}\n";  
}

Note que não estamos iterando o array dentro do objeto $books pelo foreach e sim o próprio objeto, onde o foreach irá utilizar os métodos da interface Iterator para percorrer os dados do objeto.

Este é um exemplo bem simples utilizando um array, mas você pode utilizar para qualquer tipo de estrutura de dados que desejar com regras de acesso mais complicadas, como filtros e etc.

Caso não queira implementar todos os métodos da interface Iterator é possível implementar a interface IteratorAggregate em nossa classe. Esta interface defini um método getIterator que deve retornar um objeto Iterator, o qual podemos utilizar algum dos fornecidos pelo PHP como o ArrayIterator ou mesmo criarmos um próprio. Modificamos o código do BooksCollection acima para utilizar o ArrayIterator.

final class BooksCollection implements IteratorAggregate
{
    private array $books;

    public function insert(Book $book): void
    {
        $this->books[] = $book;
    }

    public function getIterator(): Traversable
    {
        return new ArrayIterator($this->books);
    }  
}

A propósito o SPL fornece diversos Iterators prontos como:

DirectoryIterator: intera sobre o diretório passado no seu construtor.

$dir = new DirectoryIterator(__DIR__);
foreach ($dir as $file) {
    echo $file->getPathname(). PHP_EOL;
    echo $file->getFilename(). PHP_EOL;
}

FilterIterator: é uma classe abstrata para iterar sobre outro iterador realizando um filtro, este filtro é definido através da sobreescrita do método accept onde ele retornando true o valor será iterado se false não será. É parecido com a função array_filter.

$values = [1,2,3,4,5,6,7,8,9,10];

final class ParFilterIterator extends FilterIterator
{
    public function accept(): bool
    {
        return $this->current() % 2 == 0;
    }
}   

$iterator = new ArrayIterator($values);
$parIterator = new ParFilterIterator($iterator);

foreach($parIterator as $value){
    echo $value . PHP_EOL;
}

RegexIterator: é usado para iterar sobre outro iterador baseado em uma expressão regular. Ele recevebe um Iterator, o pattern e o mode que pode ser: RegexIterator::ALL_MATCHES( retorna todos os matches – preg_match_all()), RegexIterator::GET_MATCH(retorno o primeiro match – preg_match()), RegexIterator::REPLACE(substitui o valor encontrado por um novo, preg_replace()).

$a = new ArrayIterator(['rodrigo@botecodigita.dev.br', 'maria@gmail.com', 'andre@hotmail.com', 'julia@gmail.com']);
$i = new RegexIterator($a, '/^(.+)@(.+)/', RegexIterator::REPLACE);
$i->replacement = '$1(at)$2';

foreach($i as $value){
    echo $value . PHP_EOL;
}

O SPL fornece muitos iterators prontos, para mais veja na documentação.

Interface Countable

Ao implementar a interface Countable você será capaz de utilizar a função count para saber quantos elementos a estrutura tem. A interface Countable possui um método count() que será chamado quando a função count for chamada para um objeto que implemente esta interface.

Interface ArrayAccess

A interface ArrayAccess permite que se acesse um objeto como se ele fosse um array. Para isto é necessário implementar quatro métodos.

offsetExistsVerifica se uma posição existe
offsetGetRecupera o valor de uma determinada posição
offsetSetAtributi um valor para uma determinada posição
offsetUnsetDestrói uma posição

Por exemplo se tentarmos usar um dos atributos da classe Book que vimos acima como um array($book['name']) ele dará um erro “Uncaught Error: Cannot use object of type Book as array“, mas se implementarmos a interface ArrayAccess podemos utilizar a sintaxe de acesso a array com qualquer objeto.

final class Book implements ArrayAccess
{
    public string $name;
    public string $author;
    public function __construct(string $name, string $author)
    {
        $this->name = $name;
        $this->author = $author;
    }

    public function offsetExists(mixed $offset): bool
    {
        return $offset === 'name' || $offset === 'author';
    }
    public function offsetGet(mixed $offset): mixed
    {
        return $this->{$offset};
    }
    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->{$offset} =  $value;
    }
    public function offsetUnset(mixed $offset): void
    {
        throw RuntimeException('Não pode ser deletado!');
    }
}

$book = new Book('Senhor dos Anéis','J. R. R. Tolkien');

echo $book['name'];
echo $book['author'];

SplDoublyLinkedList

SPL - Representação de uma SplDoublyLinkedList
SPL – Lista Ligada

Uma lista ligada é uma lista onde cada valor possui um link para os valores próximo e anterior e estes são utilizados para navegar entre os valores na estrutura. No PHP esta estrutura de dados é implementada através da classe SplDoublyLinkedList que implementa as interfaces Iterator, ArrayAccess e Countable. Os principais métodos da SplDoublyLinkedList são:

  • push(mixed $value): void – Adiciona um valor ao final da lista.
  • add(int $index, mixed $value): void – Adiciona um valor na posição index.
  • bottom(): mixed – retorna o primeiro valor da lista.
  • shift(): mixed – remove e retorna o primeiro valor da lista.
  • count(): int – retorna a quantidade de valores da lista
  • isEmpty(): bool – retorna se a lista está vazia(true) ou não(false)
  • top(): mixed – retorna o valor do final da lista
  • pop(): mixed – remove e retorna o valor do final da lista.
$dlist = new SplDoublyLinkedList();

$dlist->push(1);
$dlist->push(2);
$dlist->push(3);

foreach($dlist as $value){
    echo $value. PHP_EOL;
}

$dlist->add(2,4);

echo 'Total '. $dlist->count(). PHP_EOL;

foreach($dlist as $value){
    echo $value. PHP_EOL;
}

while( !$dlist->isEmpty()){
    echo $dlist->shift() . PHP_EOL;;
}

SplQueue

A classe SplQueue implementa uma estrutura de fila, que basicamente é uma lista onde só é perimitido inserir no início da lista e remove no final, também conhecido como FIFO(First in Fist Out/primeiro a entrar primeiro a sair). Ela fornece os métodos:

  • enqueue(mixed $value): void – adiciona um valor no final da fila
  • dequeue(): mixed – remove e retorna um valor do inicio da fila
  • count(): int – retorna a quantidade de valores que contem na fila

A SplQueue estende a classe SplDoublyLinkedList então tecnicamente podemos chamar seus métodos embora não seja interessante pois estariamos manipulando como uma lista e não uma fila que é mais especifica.

$queue = new SplQueue();

$queue->enqueue(1);
$queue->enqueue(2);
$queue->enqueue(3);

echo $queue->dequeue() . PHP_EOL; //retorna 1
echo $queue->dequeue() . PHP_EOL; //retorna 2

$queue->enqueue(4);

echo $queue->dequeue() . PHP_EOL; //retorna 3
echo $queue->dequeue() . PHP_EOL; //retorna 4

SplStack

A classe SplStack implementa uma estrutura de pilha, onde os valores são só inseridos e removidos do final, também é conhecido como LIFO(last in first out/último a entrar primeiro a sair). Você vai notar que ao iterar sobre uma SplStack ele irá começar a percorrer pelo final da lista. Esta classe estende da SplDoublyLinkedList e não adiciona nenhum novo método, então utilizamos o push para adicionar no final da lista(topo), pop retornar e remover e top para apenas retornar o valor do topo.

$stack = new SplStack();

$stack->push(1);
$stack->push(2);
$stack->push(3);

foreach($stack as $value){
    echo $value. PHP_EOL;
}

echo $stack->pop() . PHP_EOL;; // retorna 3 e remove
echo $stack->pop() . PHP_EOL;; // retorna 2 e remove
echo $stack->pop() . PHP_EOL;; // retorna 1 e remove

echo 'Total: ' . $stack->count();

Heap

Heaps são estruturas de dados tipo árvore binária(cada nó só pode ter zero, um ou dois filhos). Estas estruturas são utilizadas para manter os valores em um determinada ordem. A Spl fornece a classe SplHeap que é uma classe abstrata no qual devemos sobreescrever o método protected compare(mixed $value1, mixed $value2): int. Para um simples ordem crescente e decrescente de valores é fornecidas as classes SplMinHeap e SplMaxHeap.

$heap = new SplMinHeap();

$heap->insert(5);
$heap->insert(3);
$heap->insert(7);
$heap->insert(4);
$heap->insert(-1);

foreach($heap as $value){
    echo $value . ' ';
}

//Saida: -1 3 4 5 7
$heap = new SplMaxHeap();

$heap->insert(5);
$heap->insert(3);
$heap->insert(7);
$heap->insert(4);
$heap->insert(-1);

foreach($heap as $value){
    echo $value . ' ';
}

//Saida: 7 5 4 3 -1

Criando um Heap com o próprio comparador para objetos

final class Book
{
    public string $name;
    public string $year;
    public function __construct(string $name, int $year)
    {
        $this->name = $name;
        $this->year = $year;
    }
}

final class BookHeap extends SplHeap{
    protected function compare(mixed $book1, mixed $book2): int
    {
        return  $book2->year <=> $book1->year;
    }
}

$bookHeap = new BookHeap();
$bookHeap->insert( new Book('A Guerra dos Tronos', 1996) );
$bookHeap->insert( new Book('Senhor dos Anéis',1955) );
$bookHeap->insert( new Book('A Caçada ao Outubro Vermelho', 1984) );
$bookHeap->insert( new Book('A Guerra dos Tronos', 2014) );

foreach($bookHeap as $book){
    echo $book->name .' - '. $book->year. PHP_EOL; 
}

SplFixedArray

A estrutura SplFixedArray armazena os dados de maneira continua e acessível através de arrays. Ele é mais rápido que arrays nomais, mas menos flexiveis pois tem o tamanho fixo e só podem utilizar indices inteiros. A SplFixedArray implementa as interfaces Iterator e ArrayAccess

$array = new SplFixedArray(1_000_000);

$timeSplStart = round(microtime(true)*1000);
for($i = 0 ; $i < 1_000_000; $i++){
    $array[$i] = $i;
}
$timeSplEnd = round(microtime(true)*1000);
echo 'SplFixedArray Time: ' . ($timeSplEnd - $timeSplStart);

echo PHP_EOL;

$array = [];
$timeArrayStart = round(microtime(true)*1000);
for($i = 0 ; $i < 1_000_000; $i++){
    $array[$i] = $i;
}
$timeArrayEnd = round(microtime(true)*1000);
echo 'Array Time: ' . ($timeArrayEnd - $timeArrayStart);

//SplFixedArray Time: 168
//Array Time: 174

SplObjectStorage

A SplObjectStorage é um estrutura do tipo mapa que mapeia objetos para dados. A classe SplObjectStorage fornece os métodos

  • attach(object $object, mixed $info = null) – adiciona um dado no mapa, onde $object é chave/indice e $info é o valor a ser armazenado.
  • contains(object $object): bool – verifica se o mapa contem o objeto passado.
  • detach(object $object) – remove um objeto do mapa.
  • getInfo(): mixed – retorna o dado associado ao objeto atual do iterador.
$map = new SplObjectStorage();

$user = new StdClass;
$user->name = "Rodrigo";
$user->username = "orodrigo";

$metaData = 'value';

$map->attach($user, $metaData);

$map->rewind();
while($map->valid()) {
    $objectKey = $map->current();
    $data   = $map->getInfo();

    echo  $objectKey->name . ' '. $objectKey->username . PHP_EOL;
    echo $data . PHP_EOL;

    $map->next();
}

echo ($map->contains($user) ? "Sim": "Não")  . PHP_EOL;

echo $map[$user] . PHP_EOL;

$map->detach($user);

echo ($map->contains($user) ? "Sim": "Não")  . PHP_EOL;

Exceções

A SPL também fornece diversas exceções:

  • InvalidArgumentException – Exceção lançada se um argumeto não é do tipo esperado.
  • RuntimeException – Exceção lançada se um erro que somente pode ser encontrado em tempo de execução ocorrer.
  • OutOfBoundsException – Exceção lançada se um valor não é uma chave válida.

Veja as outras na documentação.

Manipulando arquivos

A Spl também fornece uma interface orintada a objetos para manipular um arquivo. Isto é feito através de duas classes, SplFileObject e SplFileInfo.

$file = new SplFileObject("temp.txt", "w");
$written = $file->fwrite("uma dado\n");
$written = $file->fwrite("outro dado");
$file->fflush();
$file = new SplFileObject("temp.txt", "r");

while(!$file->eof()){
    echo $file->fgets();
}

No construtor do SplFileObject recebemos o caminho do arquivo e modo que queremos manipular o arquivo. O método fwrite serve para gravar informações no arquivo e o fgets para ler informações dele. O método eof retorna true se o arquivo já chegou ao seu final.

Podemos pegar informações sobre um arquivo através da classe SplFileInfo.

$fileinfo = new SplFileInfo('temp.txt');

echo $fileinfo->getFilename() . PHP_EOL;
echo $fileinfo->getSize() . PHP_EOL;
echo $fileinfo->getType() . PHP_EOL;
echo $fileinfo->isWritable() . PHP_EOL;
echo $fileinfo->isDir() . PHP_EOL;
echo $fileinfo->getExtension() . PHP_EOL;

Bom era isso uma pequena introdução ao Spl a algumas classes e interfaces que ele fornece, mais informações consulte a documentação. T++