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;
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;
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";
}
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";
}
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";
}
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.
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);
But using foreach
, it's even simpler:
<?php
$nums = [1, 10, 5, -6];
foreach ($nums as &$num) {
$num *= $num;
}
print_r($nums);
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);
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);
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.