Iteradores en PHP
Todos sabemos lo cómodo que es utilizar for, while y foreach al iterar sobre arrays. Este tipo de elementos del lenguaje están diseñados para recorrer arrays que no guarden en memoria grandes cantidades de datos. El problema, nuevamente, es que como son tan cómodos, sencillos y fáciles de implementar nos olvidamos de que existen mejores alternativas cuando trabajamos con datos que pueden tildar nuestros scripts.
Qué es un iterador ?
Un iterador es un objeto que permite recorrer los elementos de una colección. La interfaz estándar de un iterador en PHP es la siguiente.
interface Iterator extends Traversable {
public function current();
public function next();
public function key();
public function valid();
public function rewind();
}
Método | Descripción |
---|---|
current() | Retorna el elemento actual |
next() | Se mueve al siguiente elemento |
key() | Retorna la llave del elemento actual. |
valid() | Verifica si la posición actual es válida |
rewind() | Rebobina el iterador al primer elemento |
Cómo implementar un iterador en PHP?
Supongamos que queremos un iterador para recorrer solamente los elementos en posición par de un array. Por ejemplo, si el array es ['a', 'b', 'c', 'd']
solamente nos interesaría recorrer b
y d
. Para esto, vamos a crear un iterador desde la interfaz que provee PHP.
class EvenIterator implements \Iterator
{
private array $collection = [];
private int $key = 1;
public function __construct(array $collection)
{
$this->collection = $collection;
}
public function current()
{
return $this->collection[$this->key()];
}
public function next(): void
{
$this->key += 2;
}
public function key(): int
{
return $this->key;
}
public function valid(): bool
{
return array_key_exists($this->key(), $this->collection);
}
public function rewind(): void
{
$this->key = 1;
}
}
Con esto, podríamos recorrer de manera transparente un array de la siguiente forma.
$iterator = new EvenIterator(['a', 'b', 'c', 'd']);
while ($iterator->valid()) {
echo $iterator->current() . PHP_EOL;
$iterator->next();
}
Y obtendríamos el resultado esperado.
b
d
Incluso, dado que la interfaz Iterator
extiende de Traversable
podríamos recorrelo mediante un foreach con un resultado equivalente.
foreach ($iterator as $item) {
echo $iterator->current() . PHP_EOL;
}