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.
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.
Descubre más contenido