{
    "slug": "loop_style_preferences",
    "term": "Loop Style Preferences",
    "category": "style",
    "difficulty": "beginner",
    "short": "Choose the clearest loop construct for the job: foreach for collections, for with index when position matters, while for condition-driven iteration.",
    "long": "Loop style is a readability decision. PHP offers foreach, for, while, and do-while, plus functional alternatives like array_map, array_filter, and array_reduce. The right choice depends on intent, not personal habit.\n\nUse foreach when iterating over a collection and you only need the elements (and optionally the key). It is the default — it cannot run off the end, does not need a counter, and reads as 'for each item, do this.' Use a classic for loop only when you genuinely need an index, a step other than 1, or simultaneous traversal of related sequences. Use while when the termination condition is not tied to a collection's length (reading a stream, polling a queue, walking a linked structure). Use do-while when the body must execute at least once.\n\nFunctional alternatives (array_map, array_filter, array_reduce) express transformation intent rather than mechanics. They shine when the operation is a pure pipeline: 'take these orders, keep paid ones, sum totals.' They are not a universal replacement — multi-step loops, early exits, side effects, and stateful accumulation usually read better as foreach. Recursion is appropriate for genuinely recursive data (trees, nested structures) but is rarely the right choice for flat iteration in PHP because there is no tail-call optimisation.\n\nWithin a file, stay consistent. Mixing for ($i=0; $i<count($xs); $i++) with foreach over the same kind of data in adjacent functions is jarring. Avoid common anti-patterns: calling count() inside the for condition on a static array, using foreach with a by-reference variable and forgetting to unset it, or reaching for array_map purely to look clever when foreach would be plainer.\n\nThe guiding principle: the loop construct should communicate what the loop is doing. If a reader has to study the header to understand intent, you picked the wrong construct.",
    "aliases": [
        "iteration style",
        "for vs foreach",
        "loop choice"
    ],
    "tags": [
        "style",
        "php",
        "loops",
        "readability",
        "iteration"
    ],
    "misconception": "Functional alternatives like array_map are always more 'modern' and therefore better than foreach. In practice, foreach is often clearer for anything beyond a single pure transformation, and PHP's array functions have measurable overhead and weaker debuggability.",
    "why_it_matters": "Loop choice is one of the most frequent micro-decisions in any codebase; the wrong construct adds noise (indices you never use, counters that drift) and the right one makes intent visible at a glance.",
    "common_mistakes": [
        "Using for ($i=0; $i<count($items); $i++) when foreach ($items as $item) would do — the index is unused and count() may even be recomputed each iteration.",
        "Forgetting to unset() a by-reference variable after foreach ($items as &$item), causing the reference to leak into the next loop.",
        "Reaching for array_map/array_filter chains when the operation has side effects, early returns, or accumulates state — foreach reads better there.",
        "Mixing while (list($k, $v) = each(...)) legacy patterns with modern foreach in the same file.",
        "Using recursion for flat iteration in PHP where it adds stack frames without expressing recursive intent.",
        "Carrying over legacy while(list($k,$v)=each(...)) patterns when modernising old code instead of converting them to foreach (each() was removed in PHP 8.0)."
    ],
    "when_to_use": [
        "Choosing between for, foreach, while in new code where any would work syntactically.",
        "Refactoring index-based loops that never use the index into foreach for clarity.",
        "Deciding whether a transformation belongs in a foreach or in an array_map/filter/reduce pipeline.",
        "Establishing or documenting a team style guide for iteration patterns."
    ],
    "avoid_when": [
        "Performance-critical inner loops where a specific construct has measured wins — micro-style consistency comes second.",
        "Translating algorithms from pseudocode or another language where the original loop shape aids review and verification.",
        "Working in a legacy codebase with a strong existing convention — match it rather than introduce a second style."
    ],
    "related": [
        "early_return",
        "guard_clause",
        "deep_nesting",
        "simplify_conditionals"
    ],
    "prerequisites": [
        "psr_12",
        "early_return",
        "deep_nesting"
    ],
    "refs": [
        "https://www.php.net/manual/en/control-structures.foreach.php",
        "https://www.php.net/manual/en/control-structures.for.php",
        "https://wiki.php.net/rfc/foreach-non-iterable",
        "https://www.php.net/manual/en/function.array-map.php"
    ],
    "bad_code": "<?php\n// Index never used — foreach would be clearer\nfor ($i = 0; $i < count($orders); $i++) {\n    echo $orders[$i]->total() . \"\\n\";\n}\n\n// count() recomputed every iteration\nfor ($i = 0; $i < count($rows); $i++) {\n    process($rows[$i]);\n}\n\n// Reference leak — $item still references last element\nforeach ($items as &$item) {\n    $item = strtoupper($item);\n}\n// later...\nforeach ($items as $item) { /* surprise: writes to last element */ }\n\n// array_map abused for side effects\narray_map(function ($u) {\n    sendEmail($u);\n    log(\"sent to {$u->email}\");\n}, $users);",
    "good_code": "<?php\n// Collection iteration — foreach states intent\nforeach ($orders as $order) {\n    echo $order->total() . \"\\n\";\n}\n\n// Genuine need for index\nfor ($i = 0, $n = count($rows); $i < $n; $i++) {\n    if ($i > 0) echo \", \";\n    echo $rows[$i]->name;\n}\n\n// Reference loop with explicit cleanup\nforeach ($items as &$item) {\n    $item = strtoupper($item);\n}\nunset($item);\n\n// Condition-driven: while is correct\nwhile (($line = fgets($handle)) !== false) {\n    process($line);\n}\n\n// Pure transformation: array_map fits\n$totals = array_map(fn($o) => $o->total(), $orders);\n\n// Side effects belong in foreach\nforeach ($users as $user) {\n    sendEmail($user);\n    log(\"sent to {$user->email}\");\n}",
    "quick_fix": "Default to foreach for collections; use for only when you need the index or a non-unit step; use while for condition-driven loops; reach for array_map/filter/reduce only for pure pipelines.",
    "severity": "low",
    "effort": "low",
    "created": "2026-05-22",
    "updated": "2026-05-23",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/loop_style_preferences",
        "html_url": "https://codeclaritylab.com/glossary/loop_style_preferences",
        "json_url": "https://codeclaritylab.com/glossary/loop_style_preferences.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": "[Loop Style Preferences](https://codeclaritylab.com/glossary/loop_style_preferences) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/loop_style_preferences"
            }
        }
    }
}