Cómo escribir sus objetos iterables en PHP

Logotipo de PHP

PHP le permite crear objetos iterables. Estos se pueden usar dentro de bucles en lugar de matrices escalares. Los iterables se utilizan comúnmente como colecciones de objetos. Le permiten insertar una pista sobre el objeto mientras mantiene el soporte para el bucle.

→ Índice de contenidos

Iteración simple

Para iterar sobre una matriz en PHP, usa un archivo foreach lazo:

foreach (["1", "2", "3"] as $i) {
    echo ($i . " ");
}

Este ejemplo emitiría 1 2 3.

También puede iterar sobre un objeto:

$cls = new StdClass();
$cls -> foo = "bar";
foreach ($cls as $i) {
    echo $i;
}

Este ejemplo emitiría bar.

El problema de la cobranza

Para clases base con propiedades públicas, un simple foreach funciona bien. Consideremos ahora otra clase:

class UserCollection {
 
    protected array $items = [];
 
    public function add(UserDomain $user) : void {
        $this -> items[] = $user;
    }
 
    public function containsAnAdmin() : bool {
        return (count(array_filter(
            $this -> items,
            fn (UserDomain $i) : bool => $i -> isAdmin()
        )) > 0);
    }
 
}

Esta clase representa una colección de UserDomain instancias. Dado que PHP no admite matrices escritas, se necesitan clases como esta cuando desea escribir una sugerencia de una matriz que solo puede contener un tipo de valor. Las colecciones también le ayudan a crear métodos de utilidad, como containsWithAdmin, que facilitan la interacción natural con los elementos de la matriz.

Desafortunadamente, intentar iterar sobre esta colección no dará los resultados deseados. Idealmente, la iteración debería operar en $items matriz, no la clase en sí.

Implementación del iterador

La iteración natural se puede agregar usando el Iterator interfaz. Implementar Iterator, puede definir el comportamiento de PHP cuando se utilizan instancias de su clase con foreach.

Iterator tiene cinco métodos que necesitará implementar:

  • current() : mixed - Obtener el artículo en la posición actual en la iteración.
  • key() : scalar - Obtener la clave en la posición actual en la iteración.
  • next() : void - Pasar a la siguiente posición en la iteración.
  • rewind() : void - Rebobinar la posición hasta el comienzo de la iteración.
  • valid() : bool - Obtener si la ubicación actual tiene un valor.

Estos métodos pueden resultar confusos al principio. La implementación es simple, sin embargo, está especificando qué hacer en cada etapa de un archivo foreach ejecución.

Siempre que su artículo se use con foreach, rewind() sera llamado. los valid() se llama a continuación, informando a PHP si hay un valor en la posición actual. Si hay, current() es key() se les llama para obtener el valor y la clave en esa posición. Finalmente, el next() se llama para avanzar el puntero de posición. El ciclo vuelve a llamar valid() para ver si hay otro artículo disponible.

Aquí hay una implementación típica de Iterator:

class DemoIterator {
 
    protected int $position = 0;
 
    protected array $items = ["cloud", "savvy"];
 
    public function rewind() : void {
        echo "Rewinding";
        $this -> position = 0;
    }
 
    public function current() : string {
        echo "Current";
        return $this -> items[$this -> position];
    }
 
    public function key() : int {
        echo "Key";
        return $this -> position;
    }
 
    public function next() : void {
        echo "Next";
        ++$this -> position;
    }
 
    public function valid() : void {
        echo "Valid";
        return isset($this -> items[$this -> position]);
    }
 
}

Esto es lo que sucedería durante la iteración. DemoIterator:

$i = new DemoIterator();
foreach ($i as $key => $value) {
    echo "$key $value";
}
 
// EMITS:
// 
// Rewind
// Valid Current Key
// 0 cloud
// Next
// 
// Valid Current Key
// 1 savvy
// Next
// 
// Valid

Su iterador debe mantener un registro de la posición del bucle, verifique si hay un elemento en la posición del bucle actual (a través de valid()) y devuelve la clave y el valor en la posición actual.

PHP no intentará acceder a la clave o valor cuando la posición del bucle no sea válida. Regresando false desde valid() termina inmediatamente foreach lazo. Por lo general, esto será cuando llegue al final de la matriz.

Iterador Agregado

Escribir iteradores rápidamente se vuelve repetitivo. La mayoría sigue la receta exacta que se muestra arriba. IteratorAggregate es una interfaz que le ayuda a crear rápidamente objetos iterables.

Implementar el getIterator() método y devolver un Traversable (la interfaz básica de Iterator). Se usará como iterador cuando su objeto se use con foreach. Esta suele ser la forma más fácil de agregar iteración a una clase de colección:

class UserCollection implements IteratorAggregate {
 
    protected array $items = [];
 
    public function add(UserDomain $User) : void {
        $this -> items[] = $user;
    }
 
    public function getIterator() : Traversable {
        return new ArrayIterator($this -> items);
    }
 
}
 
$users = new UserCollection();
$users -> add(new UserDomain("James"));
$users -> add(new UserDomain("Demo"));
 
foreach ($users as $user) {
    echo $user -> Name;
}

Cuando se trabaja con colecciones, normalmente tres líneas de código son todo lo que necesita para configurar la iteración. A ArrayIterator se devuelve como Traversable. Esta es una clase que crea automáticamente un archivo Iterator de una matriz. Ahora es posible iterar sobre valores lógicos dentro del objeto, en lugar de las propiedades directas del objeto.

Usando iteradores PHP predefinidos

Deberá escribir sus iteradores si tiene una lógica compleja. Rara vez es necesario comenzar desde cero, ya que PHP viene con varios iteradores avanzados proporcionado por SPL.

Las clases integradas incluyen DirectoryIterator, FilesystemIterator, GlobIterator y varios iteradores recursivos. Estos son algunos de los iteradores genéricos más útiles.

LimitIterator

los LimitIterator le permite iterar sobre un subconjunto de una matriz. No es necesario fusionar la matriz primero o rastrear manualmente la ubicación dentro del archivo foreach.

$arr = new ArrayIterator(["a", "b", "c", "d"]);
foreach (new LimitIterator($arr, 0, 2) as $val) {
    echo $val;
}

Este ejemplo emitiría a b. Tenga en cuenta que LimitIterator aceptar otro Iterator, no una matriz. El ejemplo usa ArrayIterator, la clase incorporada que crea un archivo Iterator de una matriz.

InfiniteIterator

InfiniteIterator nunca termina el ciclo, así que tendrás que break de él manualmente. De lo contrario, el iterador vuelve automáticamente al principio de la matriz cuando llega al final.

Este iterador es especialmente útil cuando se trabaja con valores basados ​​en el tiempo. Esta es una manera fácil de crear un calendario de tres años, que también usa la extensión LimitIterator descrito arriba:

$months = [
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
];
$infinite = new InfiniteIterator(new ArrayIterator($months));
foreach (new LimitIterator($infinite, 0, 36) as $month) {
    echo $month;
}

Este emite tres meses de años.

FilterIterator

FilterIterator es una clase abstracta que necesita ampliar. Implementar el accept método para filtrar valores no deseados que deben ignorarse durante la iteración.

class DemoFilterIterator extends FilterIterator {
 
    public function __construct() {
        parent::__construct(new ArrayIterator([1, 10, 4, 6, 3]));
    }
 
    public function accept() {
        return ($this -> getInnerIterator() -> current() < 5);
    }
 
}
 
$demo = new DemoFilterIterator();
 
foreach ($demo as $val) {
    echo $val;
}

Este ejemplo emitiría 1 4 3.

Los valores de matriz superiores a 5 se filtran y no se muestran en el archivo foreach.

El tipo "iterable"

A veces puede escribir una función genérica que use un archivo foreach pero no sabe nada de los valores sobre los que iterará. Un ejemplo es un controlador de errores abstracto que simplemente vuelca los valores que recibe.

Puedes escribir la pista iterable en estos escenarios. Este es un pseudo-tipo que aceptará un archivo array o cualquier objeto implementado Traversable:

function handleBadValues(iterable $values) : void {
    foreach ($values as $value) {
        var_dump($value);
    }
}

los iterable el tipo es tan vago que debe pensarlo detenidamente antes de usarlo. Sin embargo, puede ser útil como último recurso si necesita asegurarse de que un valor de entrada funcione foreach.

Conclusión

El uso de iteradores le ayuda a escribir código limpio que es más modular. Puede mover métodos que actúan sobre matrices de objetos en clases de colección dedicadas. Estos se pueden escribir individualmente sin dejar de ser totalmente compatibles con foreach.

La mayoría de las veces, se puede agregar soporte para la iteración implementando IteratorAggregate y devolviendo un ArrayIterator configurado con los elementos de su colección. Los otros tipos de iteradores de PHP, que tienden a no ser utilizados por los desarrolladores, pueden simplificar enormemente los bucles más específicos. Ofrecen comportamientos lógicos complejos sin necesidad de realizar un seguimiento manual del puntero en su archivo. foreach declaración.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir Change privacy settings