Question
I understand what foreach is, what it does, and how to use it. My question is specifically about how foreach works internally in PHP.
For a long time, I assumed that foreach iterated over the original array itself. Later, I found many explanations saying that it works on a copy of the array instead, so I assumed that was the full story. However, after some experiments, that explanation seems incomplete.
Assume we start with this array:
$array = array(1, 2, 3, 4, 5);
Test case 1
foreach ($array as $item) {
echo "$item\n";
$array[] = $item;
}
print_r($array);
/* Output in loop: 1 2 3 4 5
$array after loop: 1 2 3 4 5 1 2 3 4 5 */
This suggests that foreach is not iterating directly over the live array structure, because otherwise the loop would keep growing forever.
Test case 2
foreach ($array as $key => $item) {
$array[$key + 1] = $item + 2;
echo "$item\n";
}
print_r($array);
/* Output in loop: 1 2 3 4 5
$array after loop: 1 3 4 5 6 7 */
This again suggests that the values being iterated are not simply read back from the array after modification.
Test case 3
var_dump(each($array));
foreach ($array as $item) {
echo "$item\n";
}
var_dump(each($array));
/* Output
array(4) {
[1]=>
int(1)
["value"]=>
int(1)
[0]=>
int(0)
["key"]=>
int(0)
}
1
2
3
4
5
bool(false)
*/
This seems to show that after foreach, the array's internal pointer is at the end.
Test case 4
foreach ($array as $key => $item) {
echo "$item\n";
each($array);
}
/* Output: 1 2 3 4 5 */
Test case 5
foreach ($array as $key => $item) {
echo "$item\n";
reset($array);
}
/* Output: 1 2 3 4 5 */
These tests suggest that changing the array pointer manually inside the loop does not affect iteration.
So what is foreach really doing internally?
- Does it iterate over a copy of the array?
- Does it interact with the original array’s internal pointer?
- Why does the pointer appear to end up at the end after the loop?
- Can pointer functions such as
each()orreset()ever affect aforeachloop’s behavior?
Short Answer
By the end of this page, you will understand the real model behind PHP foreach: it does not simply mean “loop over a full copy of the array,” and it also does not behave like while (each(...)). You will learn how foreach behaves when iterating by value vs by reference, how array modifications affect the loop, why internal pointer functions can be misleading, and what rules are safe to follow in real PHP code.
Concept
foreach in PHP is a dedicated iteration construct for arrays and traversable objects. The key idea is:
foreachmaintains its own iteration state.- It is not controlled by
reset(),next(),each(), or the array's user-visible internal pointer. - It may use PHP's internal array structure efficiently rather than making a naive element-by-element PHP-level copy.
A common beginner explanation is that foreach “loops over a copy of the array.” That explanation is useful in some cases, but it is not the full internal truth.
What foreach really guarantees
When you write:
foreach ($array as $value) {
// ...
}
PHP determines how to iterate through the elements in a way that is stable for the loop. In normal by-value iteration:
- the loop visits the elements that were part of the iteration sequence,
- appending or modifying the array during the loop usually does not make the loop continue over newly added elements,
- changing the traditional array pointer with functions like
reset()does not drive the loop.
Mental Model
Think of foreach like reading names from an attendance list.
By value
When iterating by value, it is like the teacher starts class with a prepared list of students to call:
- if someone writes extra names on the whiteboard during class, the teacher usually does not start calling those new names,
- if someone changes the seating position, it does not change the prepared calling order.
By reference
When iterating by reference, it is more like the teacher is not only reading the list but also editing the official roster directly as they go.
That means:
- changes affect the real data immediately,
- the loop variable stays connected to the actual array slot,
- mistakes can have side effects after the loop finishes.
Internal pointer functions
Functions like reset() and each() are like moving a separate bookmark in the same book.
foreach is not reading based on that bookmark. It has its own place-tracking system. So moving the bookmark does not usually change what foreach reads next.
Syntax and Examples
Basic syntax
Iterate by value
foreach ($array as $value) {
echo $value;
}
Iterate with keys and values
foreach ($array as $key => $value) {
echo $key . ': ' . $value . PHP_EOL;
}
Iterate by reference
foreach ($array as &$value) {
$value *= 2;
}
unset($value); // important
Example 1: normal by-value iteration
$numbers = [1, 2, 3];
foreach ($numbers as ) {
. PHP_EOL;
[] = * ;
}
();
Step by Step Execution
Consider this example:
$items = [1, 2, 3];
foreach ($items as $key => $value) {
echo "$key => $value" . PHP_EOL;
$items[] = $value + 100;
}
print_r($items);
Step-by-step trace
Before the loop
$items = [1, 2, 3];
The array contains three elements.
First iteration
key = 0value = 1- output:
0 => 1 - then this runs:
$items[] = 101;
Now the real array becomes:
Real World Use Cases
foreach is one of the most common constructs in PHP because arrays are used everywhere.
1. Processing request data
foreach ($_POST as $field => $value) {
echo $field . ': ' . trim($value) . PHP_EOL;
}
Used for:
- sanitizing form input,
- validating required fields,
- building error lists.
2. Formatting API responses
foreach ($users as $user) {
$result[] = [
'id' => $user['id'],
'name' => strtoupper($user['name'])
];
}
Used when transforming database or API data into response payloads.
3. Updating configuration values
foreach ($config as => ) {
( === ) {
[] = ;
}
}
Real Codebase Usage
In real codebases, foreach is usually used in predictable, low-surprise ways.
Common patterns
Guard clauses before looping
if (empty($users)) {
return [];
}
foreach ($users as $user) {
// process user
}
This avoids unnecessary work and makes intent clear.
Validation during iteration
$errors = [];
foreach ($input as $field => $value) {
if ($value === '') {
$errors[$field] = 'This field is required.';
}
}
Building a new array instead of mutating the current one
$clean = [];
foreach ($records as $record) {
$clean[] = trim();
}
Common Mistakes
1. Assuming foreach is controlled by reset() or next()
Broken assumption:
$numbers = [1, 2, 3];
foreach ($numbers as $n) {
next($numbers);
echo $n;
}
A beginner may expect this to skip values. It does not work that way.
Avoid it
Use foreach alone, or use pointer-based functions separately. Do not mix the two to control iteration.
2. Modifying the array structure and expecting intuitive results
$numbers = [1, 2, 3];
foreach ($numbers as $n) {
$numbers[] = $n;
}
This changes the array, but the loop behavior may not match your mental model.
Avoid it
Comparisons
| Concept | How it works | Affects original array values? | Uses internal pointer functions? | Best use case |
|---|---|---|---|---|
foreach ($array as $value) | Iterates by value | No | No | Reading or transforming values safely |
foreach ($array as &$value) | Iterates by reference | Yes | No | Updating array values in place |
for ($i = 0; $i < count($array); $i++) | Index-based loop | Yes, if you assign back | No | Numeric arrays when index control matters |
while (list($k, $v) = each($array)) | Pointer-driven iteration | Yes, depending on code |
Cheat Sheet
Core rules
foreachhas its own iteration state.- It does not rely on
reset(),next(), oreach()to move through elements. - By-value
foreachdoes not modify the source array unless you explicitly assign into the array. - By-reference
foreachlets you modify the original elements. - After by-reference iteration, always call
unset($value).
Syntax
foreach ($array as $value) {
// read values
}
foreach ($array as $key => $value) {
// read keys and values
}
foreach ($array as &$value) {
// modify values in place
}
unset($value);
Safe patterns
FAQ
Does PHP foreach loop over a copy of an array?
Not in the simple beginner sense of “PHP duplicates the whole array and loops over that copy every time.” A better explanation is that foreach creates its own stable iteration context.
Does reset() affect a foreach loop?
No. foreach does not use reset() or next() to decide which element comes next.
Why can I change the array inside foreach without an infinite loop?
In normal by-value iteration, foreach does not automatically keep extending its iteration to include everything you append during the loop.
When should I use foreach by reference?
Use it when you intentionally want to modify the original array values in place.
Why do I need unset($value) after foreach (&$value)?
Because the loop variable remains a reference to the last element. If you reuse that variable later, you may accidentally modify the array.
Can foreach be used with objects?
Mini Project
Description
Build a small PHP script that demonstrates the difference between iterating an array by value and by reference. This project is useful because many real bugs come from accidentally assuming that foreach updates the original array or from forgetting to clear a reference after the loop.
Goal
Create a script that shows three behaviors: reading values with foreach, modifying values by reference, and safely building a new transformed array.
Requirements
- Start with a numeric array containing at least three values.
- Use one
foreachloop by value and show that the original array does not change. - Use one
foreachloop by reference and update the original array. - Build a separate result array using another
foreachloop. - Print the arrays after each step so the behavior is visible.
Keep learning
Related questions
Converting HTML and CSS to PDF in PHP: Core Concepts, Limits, and Practical Approaches
Learn how HTML-to-PDF conversion works in PHP, why CSS support varies, and how to choose practical approaches for reliable PDF output.
How to Check String Prefixes and Suffixes in PHP
Learn how to check whether a string starts or ends with specific text in PHP using simple functions and practical examples.
How to Check if a String Contains a Word in PHP
Learn how to check whether a PHP string contains a specific word using strpos and str_contains with clear examples and common mistakes.