You can loop over arrays with foreach. PHP also lets you loop over objects, as long as they are traversable.
The iterable type
iterable is a type that accepts an array or any Traversable object. It is handy for functions that only need to loop.
<?php
function printAll(iterable $items): void
{
foreach ($items as $item) {
echo $item . " ";
}
}
printAll(["a", "b"]);
printAll(new ArrayIterator(["c", "d"]));
a b c d
IteratorAggregate: the easy way
The simplest way to make your own class loopable: implement IteratorAggregate and return an iterator from getIterator().
<?php
class Playlist implements IteratorAggregate
{
private array $songs = [];
public function add(string $song): void
{
$this->songs[] = $song;
}
public function getIterator(): Iterator
{
return new ArrayIterator($this->songs);
}
}
$list = new Playlist();
$list->add("Sunrise");
$list->add("River");
foreach ($list as $i => $song) {
echo ($i + 1) . ". $song\n";
}
1. Sunrise
2. River
The playlist keeps its data private, and foreach still works.
Iterator: full control
An Iterator implements five methods: current, key, next, rewind and valid. Use it when you need your own way of walking.
<?php
class Countdown implements Iterator
{
private int $current;
public function __construct(private int $from) {}
public function rewind(): void { $this->current = $this->from; }
public function valid(): bool { return $this->current > 0; }
public function current(): int { return $this->current; }
public function key(): int { return $this->from - $this->current; }
public function next(): void { $this->current--; }
}
foreach (new Countdown(3) as $n) {
echo $n . " ";
}
3 2 1
In practice, a generator (next lesson) is usually shorter.
Useful built-in functions
<?php
$it = new ArrayIterator(["x" => 1, "y" => 2]);
print_r(iterator_to_array($it));
echo iterator_count($it);
Array
(
[x] => 1
[y] => 2
)
2
iterator_to_array() turns any iterable into an array. iterator_count() counts the items. iterator_apply() calls a function for each.
Countable and ArrayAccess
Two more interfaces make a class feel like an array.
<?php
class Bag implements Countable, ArrayAccess
{
private array $items = [];
public function count(): int { return count($this->items); }
public function offsetExists(mixed $offset): bool { return isset($this->items[$offset]); }
public function offsetGet(mixed $offset): mixed { return $this->items[$offset] ?? null; }
public function offsetSet(mixed $offset, mixed $value): void
{
if ($offset === null) {
$this->items[] = $value;
} else {
$this->items[$offset] = $value;
}
}
public function offsetUnset(mixed $offset): void { unset($this->items[$offset]); }
}
$bag = new Bag();
$bag[] = "apple";
$bag["fav"] = "mango";
echo count($bag) . " items, favourite: " . $bag["fav"];
2 items, favourite: mango
Where you meet them
- Database result sets and collections in frameworks.
- The Standard PHP Library (SPL):
ArrayObject,SplStack,SplQueue,RecursiveDirectoryIterator. - Generators, covered next.