An iterable is a value that PHP can traverse with foreach. The iterable type accepts arrays and objects implementing Traversable. A generator is one such object: a function using yield produces values as the caller requests them, rather than building a complete array before returning.
Lazy production can reduce memory use for large sequences, but it changes when code runs and whether the sequence can be traversed again. This guide explains those trade-offs with small numeric examples and shows how a function can consume either an array or a generator through one iterable contract.
iterable describes a capability
An iterable parameter says the function needs traversal. It does not promise array indexing, countability or repeatable traversal. An array supports direct key access, while a generator generally does not. Avoid writing $values[0] inside a function that claims to accept any iterable.
Use foreach for the common capability. If the operation genuinely needs random access or repeated traversal, request an array or materialize the iterable deliberately. That conversion costs memory and can consume a one-pass source. Make the reason visible rather than pretending every iterable behaves like a list already stored in memory.
The iterable type has existed since PHP 7.1 and represents arrays or Traversable objects. Newer PHP versions formalized related type relationships, but the practical beginner contract remains traversal. It does not validate each element's type; check or document element expectations separately.
yield suspends a generator
A function containing yield returns a Generator object when called. The function's body does not immediately run to completion. When iteration begins, it runs until a yield supplies a value, then suspends. The next iteration resumes from that point and continues until another yield or the end.
yield $value supplies a value with an automatically generated key. yield $key => $value supplies both parts. A final return is not another yielded item. It ends the generator, and specialized consumers can inspect its return value after completion using the appropriate API.
This timing matters for errors and resources. An exception inside the body may arise during foreach rather than when the generator was created. Code opening a file can also run only when traversal begins. Place cleanup in a try/finally structure when the generator owns a resource, and understand what happens if the consumer stops early.
Lazy does not automatically mean cheap
A generator avoids storing its own full output sequence, but any large input already held in memory still exists. Wrapping an existing million-row array in yield does not undo the memory used by that array. The producer must also be incremental, such as reading one line or database row at a time with an appropriate driver configuration.
The consumer can cancel the benefit by collecting every value into another array. Functions such as iterator_to_array deliberately materialize the sequence. That can be the right choice when later work needs sorting or random access, but it restores a full-result memory cost. Measure the complete producer-consumer pipeline rather than just the generator function.
Generators also retain their suspended local state. A yielded sequence that holds a large object or buffer can still use substantial memory. Keep retained state small and release resources predictably. Lazy iteration is a useful design tool, not a blanket promise of low memory or faster execution.
Reuse, keys and delegation
A Generator is usually a one-pass object. After it advances far enough, rewinding it can fail; after completion, it is exhausted. Call the generator function again to create a fresh sequence when repeatability is needed and the source permits it. Do not traverse the same object once for counting and then expect a second traversal to produce the original values.
yield from delegates to another iterable, which is useful for combining sequences or splitting a producer into helpers. Keys can repeat across delegated sources. If you materialize with key preservation, later repeated keys can replace earlier values. Decide whether keys represent identity or whether only values matter.
The iterable reference and generator overview describe the language model. When reviewing a lazy API, ask when it performs work, who owns resources and whether callers may safely consume it more than once.
PHP Iterables and Generators Examples
Each example shows the code, the output it produces and what each part does.
Consume an array through an iterable contract
The function uses no array-only operation. Its documented expectation is integer values, although the iterable declaration alone does not enforce that element type. For untrusted inputs, validate elements or introduce a more specific collection abstraction before calculating.
<?php
function sumValues(iterable $values): int {
$total = 0;
foreach ($values as $value) {
$total += $value;
}
return $total;
}
echo sumValues([2, 3, 4]) . PHP_EOL;9Produce a small range lazily
The function yields one number at a time. It does not create an array containing the whole range. With three numbers the memory difference is irrelevant; the example exists to make the execution model clear before using it for large or streamed inputs.
<?php
function steps(int $limit): Generator {
for ($i = 1; $i <= $limit; $i++) {
yield $i;
}
}
foreach (steps(3) as $step) {
echo $step . PHP_EOL;
}1
2
3PHP Iterables and Generators in Real Projects
Practical task: sum generated quantities
Create a generator yielding three validated quantities and pass it to the iterable-based sum function. Keep the consumer independent of whether the source is an array or generator. Print the total, then demonstrate repeatability by creating a new generator rather than trying to rewind the used object.
<?php
function quantities(): Generator {
yield 4;
yield 2;
yield 7;
}
function totalQuantities(iterable $values): int {
$total = 0;
foreach ($values as $value) {
$total += $value;
}
return $total;
}
echo totalQuantities(quantities()) . PHP_EOL;
echo totalQuantities(quantities()) . PHP_EOL;13
13Each call to quantities creates a fresh Generator. The consumer uses only foreach and therefore works with a trusted array too. Replace the yields with a future line-reading source only after adding parsing, bounds and reliable cleanup. The small example separates iteration mechanics from file I/O so the expected output stays deterministic.
PHP Iterables and Generators FAQ
Is every iterable an array?
No. Traversable objects, including generators, satisfy the iterable type without supporting array indexing.
Does calling the generator run all its code?
No. It creates the generator object. Execution proceeds as the consumer advances it.
Can I sort a generator directly with sort()?
Sort expects an array. Materialize deliberately if the result is small enough, or move sorting to a source that can do it efficiently.
Can I traverse it twice?
Create a fresh generator when needed. The same Generator object is not a general reusable collection.
When should I keep an array?
When the data is modest, already loaded, and you need count, indexing or repeated passes. A generator adds little value when those requirements dominate.
PHP Iterables and Generators Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
You need another pass over a simple generator sequence. Which approach is normally appropriate?
PHP Iterables and Generators Quiz
2 questions to check what you learned.
Which keyword supplies a value from a generator?
- global
- clone
- yield
What does an iterable parameter guarantee?
- Traversal capability for arrays or Traversable objects
- Direct access to index zero
- Unlimited repeatable passes