{
    "slug": "php_object_iteration",
    "term": "Iterating Objects with foreach (Iterator & IteratorAggregate)",
    "category": "php",
    "difficulty": "intermediate",
    "short": "foreach can traverse any object implementing Iterator or IteratorAggregate, not just arrays — control iteration without exposing internal state.",
    "long": "When you write foreach ($obj as $key => $value) on an object, PHP picks one of three strategies. If the class implements Iterator, PHP calls its five methods in order: rewind(), then a loop of valid(), current(), key(), next(). If the class implements IteratorAggregate, PHP calls getIterator(), which must return a Traversable (often an inner ArrayIterator or a generator). If the class implements neither, foreach falls back to iterating the object's public properties in declaration order — useful for plain DTOs but a leaky abstraction for encapsulated classes.\n\nIterator gives you full manual control: you decide what 'current' means, how keys advance, and when iteration ends. This is ideal for streaming data, lazy database cursors, or computed sequences where you do not want to materialise everything into an array. The downside is boilerplate — five methods to maintain a cursor position correctly, including the easy-to-forget rewind() that must reset state so the same object can be looped twice.\n\nIteratorAggregate is almost always the better choice for collection classes. You implement a single getIterator() method and delegate to a generator or ArrayIterator, getting correct iteration semantics for free. Since PHP 5.5, returning a generator from getIterator() is the cleanest pattern: yield each element and PHP handles the cursor mechanics.\n\nA common subtlety: iterating an Iterator object inside nested foreach loops over the same instance shares cursor state and breaks, because there is one internal pointer. Generators and IteratorAggregate returning fresh iterators avoid this. Also note that foreach over an object's properties only sees properties visible from the calling scope, so private properties are hidden when iterating from outside the class. Prefer explicit Traversable implementations over relying on property iteration for anything beyond trivial value objects.",
    "aliases": [
        "Iterator interface",
        "IteratorAggregate",
        "Traversable objects",
        "foreach on objects"
    ],
    "tags": [
        "php",
        "iterator",
        "iteratoraggregate",
        "foreach",
        "generators",
        "oop"
    ],
    "misconception": "foreach only works on arrays, so iterating a collection means exposing an internal array property. In fact any object implementing Iterator or IteratorAggregate is traversable, letting you keep state private while supporting foreach.",
    "why_it_matters": "Implementing Traversable lets collection classes participate in foreach without leaking their internal storage, and lazy iterators avoid loading entire datasets into memory.",
    "common_mistakes": [
        "Forgetting to reset state in rewind(), so the object cannot be iterated a second time.",
        "Implementing Iterator with hand-rolled cursor methods when IteratorAggregate returning a generator would be simpler and correct.",
        "Reusing a single Iterator instance in nested foreach loops, where the shared internal pointer corrupts iteration.",
        "Relying on foreach iterating public properties as a public API, which breaks encapsulation and changes with property visibility.",
        "Returning a non-Traversable value from getIterator(), which triggers an exception at iteration time."
    ],
    "when_to_use": [],
    "avoid_when": [],
    "related": [
        "php_iterators",
        "php_spl_iterators",
        "php_generators",
        "interfaces"
    ],
    "prerequisites": [
        "interfaces",
        "php5_oop_model",
        "generators"
    ],
    "refs": [
        "https://www.php.net/manual/en/class.iterator.php",
        "https://www.php.net/manual/en/class.iteratoraggregate.php",
        "https://www.php.net/manual/en/language.oop5.iterations.php"
    ],
    "bad_code": "// Hand-rolled Iterator with a forgotten rewind reset\nclass UserCollection implements Iterator {\n    private array $users;\n    private int $pos = 0;\n\n    public function __construct(array $users) {\n        $this->users = $users;\n    }\n\n    public function current(): mixed { return $this->users[$this->pos]; }\n    public function key(): mixed { return $this->pos; }\n    public function next(): void { $this->pos++; }\n    public function valid(): bool { return isset($this->users[$this->pos]); }\n    // rewind does nothing — second foreach yields nothing\n    public function rewind(): void {}\n}\n\n$c = new UserCollection(['a', 'b']);\nforeach ($c as $u) { /* works once */ }\nforeach ($c as $u) { /* broken: pos never reset */ }",
    "good_code": "// IteratorAggregate delegating to a generator — concise and correct\nclass UserCollection implements IteratorAggregate {\n    public function __construct(private array $users) {}\n\n    public function getIterator(): Traversable {\n        foreach ($this->users as $key => $user) {\n            yield $key => $user;\n        }\n    }\n}\n\n$c = new UserCollection(['a', 'b']);\nforeach ($c as $u) { /* works */ }\nforeach ($c as $u) { /* works again: fresh generator each time */ }",
    "quick_fix": "Implement IteratorAggregate and return a generator from getIterator() instead of hand-writing the five Iterator methods.",
    "severity": "low",
    "effort": "low",
    "created": "2026-06-14",
    "updated": "2026-06-14",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/php_object_iteration",
        "html_url": "https://codeclaritylab.com/glossary/php_object_iteration",
        "json_url": "https://codeclaritylab.com/glossary/php_object_iteration.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Iterating Objects with foreach (Iterator & IteratorAggregate)](https://codeclaritylab.com/glossary/php_object_iteration) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/php_object_iteration"
            }
        }
    }
}