PHP foreach Loop

Chapter 25 9 mins

Learning outcomes:

  1. What is the foreach loop
  2. The two variants of foreach
  3. Examples using foreach
  4. Using references with foreach

What is foreach?

Besides for and while, PHP provides us with yet another control structure to conveniently perform iteration over an array (or any other iterable). It's called the foreach loop.

As the name suggests,

foreach executes a given piece of code for 'each and every' item of an array (unless obviously the flow is broken via a break or return).

foreach has two variants: one is meant to iterate over the array in terms of values only, while the other is meant to iterate over an array in terms of key/value pairs.

Here's the syntax of the first variant:

foreach ($arr as $value) statement;

We start off with the foreach keyword, followed by a pair of parentheses (()), followed by the loop's body. Inside the pair of parentheses, we begin with the array to iterate over, followed by the as keyword, followed by a variable meant to hold the current item of the array in each iteration.

By this point, such a kind of syntax might already be pretty familiar to us, i.e. a keyword, followed by a pair of parentheses containing the relevant description of the control structure, followed by a block of code associated with the control structure.

This variant is commonly used to iterate over normal, indexed arrays where only the values are relevant to us.

The second variant of foreach can be syntactically expressed as follows:

foreach ($arr as $key => $value) statement;

Everything is the same as before, just $key => $value instead of $value.

This variation is commonly used to iterate over associative arrays, where both the keys and their corresponding values are relevant.

Let's now consider a handful of examples utilizing foreach.

Examples of foreach

Suppose we have the following array of numbers and want to compute and output their sum:

<?php

$nums = [1, 10, 5, -6];

Obviously one option is to use for as follows:

<?php

$nums = [1, 10, 5, -6];

$sum = 0;
for ($i = 0, $len = count($nums); $i < $len; $i++) {
   $sum += $nums[$i];
}

echo $sum;
10

But let's give a try to foreach — it would make the code a lot simpler.

Here's the code rewritten using foreach:

<?php

$nums = [1, 10, 5, -6];

$sum = 0;
foreach ($nums as $num) {
   $sum += $num;
}

echo $sum;
10

See how we've been able to completely remove the extra details from the for loop above with the help of foreach — there's no need of a counter variable $i, or computing the length of the array, or writing the increment expression $i++, and so on; foreach takes care of all of it by itself.

And that's great!

Let's consider another example.

In the following code, we print the name of each city from the $cities array:

<?php

$cities = ['London', 'New York', 'Beijing', 'Tokyo'];

foreach ($cities as $city) {
   echo $city, "\n";
}
London New York Beijing Tokyo

foreach is simple, isn't it?

So far, we've only worked with the first variant of foreach. It's now time to experiment around with the second one.

Consider the following code:

<?php

$word_counts = [
   'amazing' => 10,
   'is' => 21,
   'short' => 3,
   'PHP' => 12,
   'tags' => 7,
];

We have an associative array where each key represents the name of a word and its corresponding value represents the count of that word in a given piece of text.

If we want to print each word with its corresponding count, we'd do the following:

<?php

$word_counts = [
   'amazing' => 10,
   'is' => 21,
   'short' => 3,
   'PHP' => 12,
   'tags' => 7,
];

foreach ($word_counts as $word => $count) {
   echo "$word ($count)\n";
}
amazing (10) is (21) short (3) PHP (12) tags (7)

But what if we only want to use the values and not the keys here? Should we use the first foreach variant then or continue using this one?

Well, we should use the first variant. If we use a value-only foreach loop (i.e. the first variant) on an associative array, it'll, as expected, only consider the values.

Shown below is an illustration:

<?php

$word_counts = [
   'amazing' => 10,
   'is' => 21,
   'short' => 3,
   'PHP' => 12,
   'tags' => 7,
];

foreach ($word_counts as $count) {
   echo "$count\n";
}
10 21 3 12 7

Remember that when using foreach, iteration happens over the given array in the exact same order in which items were originally added.

For example, in the code above, the first item's value was indeed 10 and that's the value that first got enumerated in the foreach loop. Similarly, the second item's value was 21 and, once again, that was the second value enumerated by the loop.

Using references with foreach

In the PHP References chapter, we learnt about references in PHP and how we can use them while assigning, passing and returning values.

To take it on from there, note that it's also possible to work with references in a foreach loop. In particular, the array's value could be referenced.

It's invalid to reference the key in a key-value foreach loop; only values could be referenced.

Syntactically, this could be shown as follows:

foreach ($arr as &$value) statement;

In each iteration, $value becomes an alias for the current element of $arr, i.e. changing it would change that element as well.

Let's consider an example.

Given an array of numbers, we are tasked with modifying the array in-place by replacing each number with its square. Surely, using for, this isn't that hard to do:

<?php

$nums = [1, 10, 5, -6];

for ($i = 0, $len = count($nums); $i < $len; $i++) {
   // Store the square of $nums[$i] back at the $ith position.
   $nums[$i] *= $nums[$i];
}

print_r($nums);
Array ( [0] => 1 [1] => 100 [2] => 25 [3] => 36 )

But using foreach, it's even simpler:

<?php

$nums = [1, 10, 5, -6];

foreach ($nums as &$num) {
   $num *= $num;
}

print_r($nums);
Array ( [0] => 1 [1] => 100 [2] => 25 [3] => 36 )

Keep in mind that without the ampersand (&) preceding $num in the header of foreach, this code would cease to work as expected.

In each iteration then, $num would simply hold the value of the next item in $nums, NOT its reference, and so writing to $num won't have any effect on the underlying $nums array.

Also remember that in such a kind of a loop, after the loop exits, the variable used to hold the array's values would continue holding on to the reference to the last element of the array.

If we write to this variable anywhere later on, then we'll actually be writing to the last element of the array.

An example is shown below:

<?php

$nums = [1, 10, 5, -6];

foreach ($nums as &$num) {
   $num *= $num;
}

// Writing manually to $num has an unexpected side-effect.
$num = 0;

print_r($nums);
Array ( [0] => 1 [1] => 100 [2] => 25 [3] => 0 )

In line 11, we manually set $num to 0 thinking that $num is a standalone entity not attached to any other entity. Surprisingly, that's not the case.

In each iteration of the foreach loop, $num is assigned a reference to the current element of $nums. In the last iteration, before the loop exits, $num holds a reference to the last element, and this remains as it is even after the loop exits.

Thereby, if we write to $num after the loop, we would be writing to the last element of $nums, and that's exactly what is happening above.

So what's the remedy to this?

Well, we must unset the variable by using the unset() function. Manually setting the variable to anything (even NULL), won't work; there's only one way to unset the variable and that is via unset().

Let's use unset() in the previous code and then see the difference it makes:

<?php

$nums = [1, 10, 5, -6];

foreach ($nums as &$num) {
   $num *= $num;
}

unset($num); $num = 0; print_r($nums);
Array ( [0] => 1 [1] => 100 [2] => 25 [3] => 36 )

Perfect! This time writing to $num doesn't affect the last element of $nums.

Note, however, that doing so only makes sense if we want to use the variable in a latter part of the script. If it ain't going to be used, there's actually no need to unset it.