PHP SPL – Standard PHP Library
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.
current | Retorna o elemento atual |
key | Retorna a chave do elemento atual |
next | Move para o próximo elemento |
rewind | Volta o iterator para o primeiro elemento |
valid | Verifica 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.
offsetExists | Verifica se uma posição existe |
offsetGet | Recupera o valor de uma determinada posição |
offsetSet | Atributi um valor para uma determinada posição |
offsetUnset | Destró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
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 listapop(): 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 filadequeue(): mixed
– remove e retorna um valor do inicio da filacount(): 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
Heap
s 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++