
Iterator em PHP
O Iterador é um padrão de projeto comportamental que permite a passagem sequencial através de uma estrutura de dados complexa sem expor seus detalhes internos.
Graças ao Iterator, os clientes podem examinar elementos de diferentes coleções de maneira semelhante usando uma única interface iterador.
Complexidade:
Popularidade:
Exemplos de uso: O padrão é muito comum no código PHP. Muitos frameworks e bibliotecas o usam para fornecer uma maneira padrão de percorrer suas coleções.
O PHP possui uma interface Iterator embutida que pode ser usada para criar iterators personalizados compatíveis com o restante do código PHP .
Identificação: O iterador é fácil de reconhecer pelos métodos de navegação (como next
, previous
e outros). O código cliente que usa iteradores pode não ter acesso direto à coleção que está sendo percorrida.
Exemplo conceitual
Este exemplo ilustra a estrutura do padrão de projeto Iterator. Ele se concentra em responder a estas perguntas:
- De quais classes ele consiste?
- Quais papéis essas classes desempenham?
- De que maneira os elementos do padrão estão relacionados?
Depois de aprender sobre a estrutura do padrão, será mais fácil entender o exemplo a seguir, com base em um caso de uso PHP do mundo real.
index.php: Exemplo conceitual
<?php
namespace RefactoringGuru\Iterator\Conceptual;
/**
* Concrete Iterators implement various traversal algorithms. These classes
* store the current traversal position at all times.
*/
class AlphabeticalOrderIterator implements \Iterator
{
/**
* @var WordsCollection
*/
private $collection;
/**
* @var int Stores the current traversal position. An iterator may have a
* lot of other fields for storing iteration state, especially when it is
* supposed to work with a particular kind of collection.
*/
private $position = 0;
/**
* @var bool This variable indicates the traversal direction.
*/
private $reverse = false;
public function __construct($collection, $reverse = false)
{
$this->collection = $collection;
$this->reverse = $reverse;
}
public function rewind()
{
$this->position = $this->reverse ?
count($this->collection->getItems()) - 1 : 0;
}
public function current()
{
return $this->collection->getItems()[$this->position];
}
public function key()
{
return $this->position;
}
public function next()
{
$this->position = $this->position + ($this->reverse ? -1 : 1);
}
public function valid()
{
return isset($this->collection->getItems()[$this->position]);
}
}
/**
* Concrete Collections provide one or several methods for retrieving fresh
* iterator instances, compatible with the collection class.
*/
class WordsCollection implements \IteratorAggregate
{
private $items = [];
public function getItems()
{
return $this->items;
}
public function addItem($item)
{
$this->items[] = $item;
}
public function getIterator(): Iterator
{
return new AlphabeticalOrderIterator($this);
}
public function getReverseIterator(): Iterator
{
return new AlphabeticalOrderIterator($this, true);
}
}
/**
* The client code may or may not know about the Concrete Iterator or Collection
* classes, depending on the level of indirection you want to keep in your
* program.
*/
$collection = new WordsCollection();
$collection->addItem("First");
$collection->addItem("Second");
$collection->addItem("Third");
echo "Straight traversal:\n";
foreach ($collection->getIterator() as $item) {
echo $item . "\n";
}
echo "\n";
echo "Reverse traversal:\n";
foreach ($collection->getReverseIterator() as $item) {
echo $item . "\n";
}
Output.txt: Resultados da execução
Straight traversal:
First
Second
Third
Reverse traversal:
Third
Second
First
Exemplo do mundo real
Como o PHP já possui uma interface Iterator embutida, que fornece integração conveniente com loops foreach, é muito fácil criar seus próprios iteradores para percorrer quase todas as estruturas de dados imagináveis.
Este exemplo do padrão Iterator fornece acesso fácil aos arquivos CSV.
index.php: Exemplo do mundo real
<?php
namespace RefactoringGuru\Iterator\RealWorld;
/**
* CSV File Iterator.
*
* @author Josh Lockhart
*/
class CsvIterator implements \Iterator
{
const ROW_SIZE = 4096;
/**
* The pointer to the CSV file.
*
* @var resource
*/
protected $filePointer = null;
/**
* The current element, which is returned on each iteration.
*
* @var array
*/
protected $currentElement = null;
/**
* The row counter.
*
* @var int
*/
protected $rowCounter = null;
/**
* The delimiter for the CSV file.
*
* @var string
*/
protected $delimiter = null;
/**
* The constructor tries to open the CSV file. It throws an exception on
* failure.
*
* @param string $file The CSV file.
* @param string $delimiter The delimiter.
*
* @throws \Exception
*/
public function __construct($file, $delimiter = ',')
{
try {
$this->filePointer = fopen($file, 'rb');
$this->delimiter = $delimiter;
} catch (\Exception $e) {
throw new \Exception('The file "' . $file . '" cannot be read.');
}
}
/**
* This method resets the file pointer.
*/
public function rewind(): void
{
$this->rowCounter = 0;
rewind($this->filePointer);
}
/**
* This method returns the current CSV row as a 2-dimensional array.
*
* @return array The current CSV row as a 2-dimensional array.
*/
public function current(): array
{
$this->currentElement = fgetcsv($this->filePointer, self::ROW_SIZE, $this->delimiter);
$this->rowCounter++;
return $this->currentElement;
}
/**
* This method returns the current row number.
*
* @return int The current row number.
*/
public function key(): int
{
return $this->rowCounter;
}
/**
* This method checks if the end of file has been reached.
*
* @return bool Returns true on EOF reached, false otherwise.
*/
public function next(): bool
{
if (is_resource($this->filePointer)) {
return !feof($this->filePointer);
}
return false;
}
/**
* This method checks if the next row is a valid row.
*
* @return bool If the next row is a valid row.
*/
public function valid(): bool
{
if (!$this->next()) {
if (is_resource($this->filePointer)) {
fclose($this->filePointer);
}
return false;
}
return true;
}
}
/**
* The client code.
*/
$csv = new CsvIterator(__DIR__ . '/cats.csv');
foreach ($csv as $key => $row) {
print_r($row);
}
Output.txt: Resultados da execução
Array
(
[0] => Name
[1] => Age
[2] => Owner
[3] => Breed
[4] => Image
[5] => Color
[6] => Texture
[7] => Fur
[8] => Size
)
Array
(
[0] => Steve
[1] => 3
[2] => Alexander Shvets
[3] => Bengal
[4] => /cats/bengal.jpg
[5] => Brown
[6] => Stripes
[7] => Short
[8] => Medium
)
Array
(
[0] => Siri
[1] => 2
[2] => Alexander Shvets
[3] => Domestic short-haired
[4] => /cats/domestic-sh.jpg
[5] => Black
[6] => Solid
[7] => Medium
[8] => Medium
)
Array
(
[0] => Fluffy
[1] => 5
[2] => John Smith
[3] => Maine Coon
[4] => /cats/Maine-Coon.jpg
[5] => Gray
[6] => Stripes
[7] => Long
[8] => Large
)