PHP foreach Loop: Syntax, Key-Value and Examples

The PHP foreach loop is usually the cleanest way to process every item in a PHP array. There is no counter to maintain and no array length to check. PHP simply gives you the next value until the collection is exhausted.

That simplicity is useful in real projects. It also means I rarely reach for a for loop when the job is simply “do this for every array item.” Fewer moving parts means fewer opportunities for an innocent $i++ to cause trouble.

A foreach loop can give you only the value, or both the key and value. It also works with Traversable objects, which we will see later.

PHP foreach syntax

The most common form reads each value from an iterable:

<?php
foreach ($items as $item) {
    // Use $item
}

If you also need the key, use the => syntax:

<?php
foreach ($items as $key => $item) {
    // Use $key and $item
}

PHP assigns the current element to the loop variable on each iteration. You do not need to manage an index or manually move an array pointer.

Loop through array values with foreach

When the array key is not important, keep the loop simple and read only the value.

<?php
$fruits = [
    'apple',
    'orange',
    'grapes'
];

foreach ($fruits as $fruit) {
    echo ucfirst($fruit) . PHP_EOL;
}

The output is:

Apple
Orange
Grapes

On the first iteration, $fruit contains apple. It then contains orange and finally grapes.

By default, the loop variable receives the element value. Changing that variable alone does not change the corresponding array element. Modifying elements by reference behaves differently, and we will cover that separately because it has an easy-to-miss side effect.

Get both the array key and value

For an associative array, you often need both parts of each entry. Add a key variable before =>.

<?php
$fruitPrices = [
    'apple' => 120,
    'orange' => 90,
    'grapes' => 150
];

foreach ($fruitPrices as $fruit => $price) {
    echo $fruit . ': ₹' . $price . PHP_EOL;
}

This prints:

apple: ₹120
orange: ₹90
grapes: ₹150

The same syntax works with indexed arrays. In that case, $key receives the numeric array index.

<?php
$colors = ['red', 'green', 'blue'];

foreach ($colors as $index => $color) {
    echo $index . ': ' . $color . PHP_EOL;
}

The result is:

0: red
1: green
2: blue

Modify array values with foreach

If you assign the loop value normally, PHP gives the loop variable a copy of the current value. Changing that variable does not update the original array.

<?php
$prices = [100, 200, 300];

foreach ($prices as $price) {
    $price += 10;
}

print_r($prices);

The array remains unchanged:

Array
(
    [0] => 100
    [1] => 200
    [2] => 300
)

To modify the original elements, iterate by reference by placing & before the value variable.

<?php
$prices = [100, 200, 300];

foreach ($prices as &$price) {
    $price += 10;
}

unset($price);

print_r($prices);

Now the array contains the updated values:

Array
(
    [0] => 110
    [1] => 210
    [2] => 310
)

The unset($price) line is important. After a by-reference foreach, the loop variable remains a reference to the last array element. If you reuse that variable later, you may accidentally overwrite the last item.

This is one of those small PHP details that can produce a very confusing bug from perfectly harmless-looking code.

Use break and continue inside foreach

You can use break and continue with foreach just as you would with other PHP loops.

break stops the loop completely. For example, this finds the first matching product and stops searching:

<?php
$products = [
    ['id' => 101, 'name' => 'Keyboard'],
    ['id' => 102, 'name' => 'Mouse'],
    ['id' => 103, 'name' => 'Monitor']
];

$targetId = 102;

foreach ($products as $product) {
    if ($product['id'] === $targetId) {
        echo $product['name'];
        break;
    }
}

continue skips the remaining statements in the current iteration and moves to the next item.

<?php
$numbers = [3, 8, 11, 14, 19];

foreach ($numbers as $number) {
    if ($number % 2 !== 0) {
        continue;
    }

    echo $number . PHP_EOL;
}

The output contains only the even numbers:

8
14

Loop through multidimensional arrays

A nested foreach is useful when an array contains other arrays. A common example is processing rows returned from a database or API.

<?php
$users = [
    [
        'name' => 'Anna',
        'email' => 'anna@example.com'
    ],
    [
        'name' => 'David',
        'email' => 'david@example.com'
    ]
];

foreach ($users as $user) {
    echo $user['name'] . ' - ' . $user['email'] . PHP_EOL;
}

If you genuinely need every inner key and value, you can nest another foreach:

<?php
foreach ($users as $user) {
    foreach ($user as $key => $value) {
        echo $key . ': ' . $value . PHP_EOL;
    }

    echo PHP_EOL;
}

For structured data, however, accessing known keys directly is often easier to read than adding another loop. Use nested loops when the inner structure is dynamic or when you really need to process every field.

Use foreach with objects

foreach is not limited to arrays. It can also iterate over PHP objects.

For a regular object, foreach reads the properties that are accessible from the current scope.

<?php
$user = new stdClass();
$user->name = 'Anna';
$user->email = 'anna@example.com';

foreach ($user as $property => $value) {
    echo $property . ': ' . $value . PHP_EOL;
}

The output is:

name: Anna
email: anna@example.com

Custom classes can also be made iterable by implementing interfaces such as Iterator or IteratorAggregate. In practice, this is useful when you want an object to expose a collection without exposing its internal storage directly.

Destructure array values inside foreach

When each item has the same array structure, PHP lets you unpack the values directly in the foreach declaration.

<?php
$users = [
    ['Anna', 'anna@example.com'],
    ['David', 'david@example.com']
];

foreach ($users as [$name, $email]) {
    echo $name . ' - ' . $email . PHP_EOL;
}

This is cleaner than repeatedly accessing $user[0] and $user[1].

Associative arrays can also be destructured using their keys:

<?php
$users = [
    ['name' => 'Anna', 'email' => 'anna@example.com'],
    ['name' => 'David', 'email' => 'david@example.com']
];

foreach ($users as ['name' => $name, 'email' => $email]) {
    echo $name . ' - ' . $email . PHP_EOL;
}

This works well when the expected shape of every item is known. If a required key is missing, PHP will raise a warning, so use it only with predictable data.

foreach does not depend on the array pointer

Older PHP code sometimes mixes foreach with functions such as current(), next(), and reset(). That can make it look as though foreach uses the array’s internal pointer.

It does not. A foreach loop manages its own iteration state.

<?php
$colors = ['red', 'green', 'blue'];

next($colors);

echo current($colors) . PHP_EOL;

foreach ($colors as $color) {
    echo $color . PHP_EOL;
}

The first line printed by current() is green, because the internal pointer was moved. The foreach loop still starts from red and processes the complete array.

This also means there is no need to call reset() before a foreach loop.

Alternative foreach syntax in templates

PHP also supports an alternative syntax using endforeach. It is especially readable when PHP is mixed with HTML.

<?php
$products = ['Keyboard', 'Mouse', 'Monitor'];
?>

<ul>
    <?php foreach ($products as $product): ?>
        <li><?= htmlspecialchars($product, ENT_QUOTES, 'UTF-8') ?></li>
    <?php endforeach; ?>
</ul>

The colon syntax behaves the same as the brace syntax. The benefit is mostly readability in templates, where several closing braces can quickly become hard to scan.

Common foreach mistakes

foreach is simple, but a few mistakes appear often enough to deserve attention.

Forgetting to unset a reference

If you iterate by reference, unset the loop variable when the loop finishes.

<?php
$values = [10, 20, 30];

foreach ($values as &$value) {
    $value *= 2;
}

unset($value);

Without unset($value), the variable remains linked to the last array element.

Using foreach on a non-iterable value

foreach expects an array or an iterable object. If the value may be missing or have another type, validate it before looping.

<?php
$items = getItems();

if (is_iterable($items)) {
    foreach ($items as $item) {
        echo $item . PHP_EOL;
    }
}

is_iterable() is useful when a function may return either an array or an object that implements Traversable.

Modifying an array while iterating over it

Adding, removing, or reordering elements inside the same foreach can make the code difficult to reason about, especially when references are involved.

If the goal is to transform an array, it is often clearer to build a new array or deliberately update known elements by key.

<?php
$prices = [
    'keyboard' => 1500,
    'mouse' => 700
];

foreach ($prices as $product => $price) {
    $prices[$product] = $price * 1.10;
}

This approach is explicit. The key tells you exactly which original element is being updated.

Using foreach when you only need to find one item

A foreach loop is perfectly fine for searching a small collection, but remember to stop once the result is found.

<?php
$users = [
    ['id' => 10, 'name' => 'Anna'],
    ['id' => 20, 'name' => 'David'],
    ['id' => 30, 'name' => 'Maya']
];

$foundUser = null;

foreach ($users as $user) {
    if ($user['id'] === 20) {
        $foundUser = $user;
        break;
    }
}

Without break, PHP would continue checking the remaining elements even though the result is already known.

foreach vs for loop in PHP

Use foreach when you want to process the elements of an array or iterable collection. Use for when the loop naturally depends on a counter, range, or numeric position.

For example, this is a natural use of foreach:

<?php
$users = ['Anna', 'David', 'Maya'];

foreach ($users as $user) {
    echo $user . PHP_EOL;
}

A for loop makes more sense when the index itself is part of the task:

<?php
for ($page = 1; $page <= 5; $page++) {
    echo 'Loading page ' . $page . PHP_EOL;
}

You can certainly loop through an indexed array with for, but if you do not need the numeric position, foreach usually expresses the intention more clearly.

foreach vs array functions

Not every array operation needs a loop. PHP also provides functions such as array_map(), array_filter(), and array_reduce().

For a simple transformation, array_map() can be concise:

<?php
$prices = [100, 200, 300];

$pricesWithTax = array_map(
    static fn (int $price): float => $price * 1.10,
    $prices
);

But foreach is often easier to read when the processing has multiple conditions, several statements, logging, early exits, or updates to more than one value.

Do not replace a clear three-line foreach with a clever chain of array functions just because you can. Readability still wins.

PHP foreach FAQ

Can foreach loop through both indexed and associative arrays?

Yes. With an indexed array, the key is normally a numeric index. With an associative array, the key can be a string.

<?php
$user = [
    'name' => 'Anna',
    'role' => 'Editor'
];

foreach ($user as $key => $value) {
    echo $key . ': ' . $value . PHP_EOL;
}

Does foreach change the original array?

Not when you iterate over values normally. Assigning a new value to the loop variable does not change the corresponding array element.

To modify the original values directly, iterate by reference with &$value. Remember to call unset($value) after the loop.

Can foreach iterate over objects?

Yes. It can iterate over accessible properties of ordinary objects and over objects that implement Traversable, including Iterator and IteratorAggregate.

How do I get the current index in foreach?

Use the key-value form:

<?php
$colors = ['red', 'green', 'blue'];

foreach ($colors as $index => $color) {
    echo $index . ': ' . $color . PHP_EOL;
}

For a normal indexed array, $index will contain 0, 1, 2, and so on. Do not assume this sequence if array elements have previously been removed or custom numeric keys are used.

How do I stop a foreach loop?

Use break to leave the loop completely. Use continue when you only want to skip the current element and move to the next one.

Do I need reset() before foreach?

No. foreach does not rely on the array’s internal pointer, so moving that pointer with functions such as next() or current() does not change where a foreach loop begins.

Should I use each() instead of foreach?

No. The old each() function was deprecated in PHP 7.2 and removed in PHP 8.0. Modern PHP code should use foreach instead.

Conclusion

For iterating through arrays and iterable objects, foreach is usually the most direct PHP loop. Use the value-only syntax when that is all you need, and the $key => $value form when the key matters.

Use references only when you intentionally want to modify the original array, and always clean up the reference afterward. For most application code, keeping the loop simple makes its purpose immediately obvious when you return to it months later.

Photo of Vincy, PHP developer
Written by Vincy Last updated: August 11, 2026
I'm a PHP developer with 20+ years of experience and a Master's degree in Computer Science. I build and improve production PHP systems for eCommerce, payments, webhooks, and integrations, including legacy upgrades (PHP 5/7 to PHP 8.x).

Continue Learning

These related tutorials may help you continue learning.

Leave a Reply

Your email address will not be published. Required fields are marked *

Explore topics
Need PHP help?