Introduction
Since PHP takes a lot of its inspiration from C, it's no surprise to learn that it comes equipped with another iteration structure besides for
— the while
loop.
In this chapter, we aim to unravel the purpose behind while
in addition to considering an array of various examples of its usage. Without any single doubt, while
forms a crucial part of modern-day programming. Many many algorithms are powered by the powerful nature of while
.
So what is while
? Let's see it...
What is while
meant for?
By design:
while
loop is meant to repeatedly execute a piece of code an unknown number of times.For instance, if we want to print 'Hello' until the user enters '-1' in an input, what we need is the while
loop.
Similarly, if we want to iterate over a queue, removing items one-by-one, until there is no item left, this is also meant to be done using a while
loop.
At this point, it's worthwhile mentioning that anything that can be done, or is done, using while
can also be done using for
, and vice versa. It's just that the syntax of while
sometimes suits the situation more and obviously asks for less setup than a for
loop.
Let's now explore the syntax of while
.
Syntax of while
The while
loop begins with the while
keyword, followed by a pair of parentheses (()
) encapsulating the condition of iteration, followed by the loop's body.
Here's a programmatic view of this syntax:
<?php
while (condition) statement;
The loop continues iterating as long as the given condition
remains true
. The moment it becomes false
, the loop ends.
If you recall, this is identical to how the condition in a for
loop works — that is, if it's true, the next iteration happens, or else the loop ends.
With the syntax also done, it's finally time to consider some quick examples.
Examples
Suppose we want to compute the sum a couple of positive numbers entered by the user.
The way the entry happens is via an input prompt for the first number, followed by another input prompt for the second number, and so on, until the value entered is -1
. -1
signals the end of the whole stream of positive numbers to add.
Such a special value is often referred to as a sentinel. A loop that iterates up until the point a sentinel is seen is, likewise, referred to as a sentinel-controlled loop.
Anyways, this task can very easily be accomplished with the help of while
as shown below:
<?php
$sum = 0;
echo 'Enter a number, -1 to exit> ';
$input = rtrim(fgets(STDIN));
while ($input !== '-1') {
$sum += (int) $input;
echo 'Enter a number, -1 to exit> ';
$input = rtrim(fgets(STDIN));
}
echo "The sum is $sum.";
We start off by defining a variable $sum
to hold the sum of the input numbers. Next we set up an input prompt and then a while
loop that should execute only if the number entered is not -1
.
If the loop's condition is met, the entered number is added to $sum
, followed by the same input prompt that we had before the loop. In the end, the sum of the input numbers is output.
Here's an illustration of the program:
Quite simple.
If we were to accomplish the same thing using for
, we'd go like:
<?php
echo 'Enter a number, -1 to exit> ';
$input = rtrim(fgets(STDIN));
for ($sum = 0; $input !== '-1'; ) {
$sum += (int) $input;
echo 'Enter a number, -1 to exit> ';
$input = rtrim(fgets(STDIN));
}
echo "The sum is $sum.";
See how the syntax of for
is a little bit less intuitive and a little more to type. In this case, while
is definitely the better choice.
As another example, suppose we want to remove elements from the end of an array, using the array_pop()
function, until it becomes empty. This also can be done nicely using a while
loop.
Consider the following code:
<?php
$arr = [1, 3, 5];
while (count($arr) !== 0) {
echo array_pop($arr), "\n";
}
The condition count($arr) !== 0
checks if the array has anything remaining in it. If there is something remaining, the condition evaluates to true
, and hence the loop's body executes, thereby removing the last element from the array (via the array_pop()
call).
Note that array_pop($arr)
effectively reduces the length of $arr
as the loop progresses and, thus, takes it closer and closer to the success of the condition count($arr) !== 0
. Without this function, we'd get an infinite loop.
Although the code above works perfectly, you'll often see one common convention used out there to check whether an array has elements remaining within it.
That is, for a given array $arr
, instead of using count($arr) !== 0
to check whether there are any remaining elements in $arr
, the shortened expression count($arr)
is used.
When it coerces to true
, it simply means that the array's length is not 0
and hence there is still something remaining in it. This is effectively identical to count($arr) !== 0
.
Likewise, the code above can be rewritten as follows:
<?php
$arr = [1, 3, 5];
while (count($arr)) {
echo array_pop($arr), "\n";
}
The while
loop here can be read as: "while the array has some length, keep removing its last element."
count($arr)
returns a positive number (which means that $arr
has some elements in it), it would coerce to the Boolean value true
and, thus, get the loop to be executed.The do...while
loop
One often-overlooked variation of the while
loop, with a sligtly different syntax, is the do...while
loop. It serves a crucial purpose that the typical while
doesn't.
That is,
do...while
first evaluates the loop's body and then checks the condition (for the next iteration).Hence, when the given condition is false to begin with, do...while
still executes the body once.
First, let's see the syntax of do...while
:
<?php
do statement;
while (condition);
As before, statement
is the loop's body whereas condition
is the iteration condition.
;
) at the end of the do
statement. That's because after the first semicolon (which is optional), the do
statement ends, and likewise a while
is expected before the second semicolon. Putting two semicolons one after another simply misses out the while
statement, and this is the reason why doing so is syntactically invalid.Now you might ask what's the benefit of this approach?
Well, sometimes we do want to execute a certain piece of code and, only after that, check for some condition before executing the code again.
A common example would be obtaining an input again and again, as long as the entered value is valid. Such a loop that runs until an entered value is valid is often referred to as an input-validation loop.
Consider the following code:
<?php
do {
echo 'Enter a number> ';
$input = rtrim(fgets(STDIN));
}
while (!is_numeric($input));
echo "You entered the number $input";
First we have the do
block, asking for the user's input which must be a valid number. Next, in the while
part of the loop, we check whether the input represents a number or not, thanks to the is_numeric()
function. If it doesn't, this means that the provided input was not a number and so, likewise, we must repeat the prompt.
In words, we can read the while
loop's condition as "keep asking for the user's input while the entered value is not a numeric value."
Note that the code above can be rewritten using a plain while
loop as well, with just a little more code. Below shown is an illustration:
<?php
echo 'Enter a number> ';
$input = rtrim(fgets(STDIN));
while (!is_numeric($input)) {
echo 'Enter a number> ';
$input = rtrim(fgets(STDIN));
}
echo "You entered the number $input";
Notice that we have to repeat the loop's body before the actual while
statement. This is so that $input
can be set to a particular value before it's inspected in the following loop.
One obvious way out of this repetition is to predefine the variable $input
with a dummy value that'll at least get the first iteration of the loop to execute.
For instance, we can manually set $input
to the dummy string 'abc'
before while
executes. This would get the loop's condition to be met, thereby leading to the display of the input prompt, after which $input
will be evaluated once again for the next iteration:
<?php
$input = 'abc';
while (!is_numeric($input)) {
echo 'Enter a number> ';
$input = rtrim(fgets(STDIN));
}
echo "You entered the number $input";
This technique surely works, but it's not really that intuitive.
Ideally, we should use do...while
for such a scenario since it was made purposefully for such a scenario. Moreover, with this approach, we have to predeclare the variables involved in the loop's condition before the while
statement.
This doesn't have to be done with a do...while
loop — the respective variables can be defined right inside the do
block and then remain accessible in the while
clause.
To boil it down, whenever we wish to execute a piece of code repeatedly, checking the condition after the code, we must use a do...while
loop. It might be an occasional programming construct but, when used, is extremely versatile and elegant.
Nested loops
What could possibly be more interesting in a program than a set of nested loops. And if those loops all belong to the while
family, things become even more interesting.
So what do you think? Shall we consider a complicated example featuring nested loops?
Let's suppose we want to ask the user to input a list of numbers.
Each number is entered in a separate input prompt. If the entered value is not a number, an error message is output, followed by asking the user to input again. This goes on until the sentinel value q
is entered, marking the the end of the whole list.
Here's the code that does this:
<?php
$sum = 0;
do {
// Obtain the correct input.
do {
echo "Enter a number, 'q' to end> ";
$input = rtrim(fgets(STDIN));
if (!is_numeric($input) && $input !== 'q') {
echo "Entered value is invalid.\n";
}
}
while (!is_numeric($input) && $input !== 'q');
// Add the input, if not 'q'.
if ($input !== 'q') {
$sum += $input;
}
}
while ($input !== 'q');
echo "The sum is $sum.";
The outer loop is a sentinel-controlled loop that iterates up until the point the value q
is input. With each iteration of this outer loop, the entered value is added to the previous sum stored in $sum
, but only if the entered value is not q
(lines 17 - 19).
Contrary to this, the inner loop is an input-validation loop that iterates up until the point the value entered is valid. Once the input is valid, the inner loop ends and likewise provides the correct value of $input
to the following code, i.e. line 17 where the conditional addition takes place.
Here's a demonstration of running this program:
As desired, when we input a value that's nor a valid number and neither the letter 'q', we get the error message output. Moreover, once q
is entered, the sum of the numbers input uptil that point is output.
Nested loops are awesome.
Note that the task above can also be accomplished without using a set of nested loops. Here's one way to rewrite the code above:
<?php
$sum = 0;
do {
echo "Enter a number, 'q' to end> ";
$input = rtrim(fgets(STDIN));
// Skip the iteration if the input is not valid.
if (!is_numeric($input) && $input !== 'q') {
echo "Entered value is invalid.\n";
continue;
}
// Add the input, if not 'q'.
if ($input !== 'q') {
$sum += $input;
}
}
while ($input !== 'q');
echo "The sum is $sum.";
The inner loop is gone and in place of that we have a simple condition to check if the input value is invalid. If it is invalid, the error message is output and then the current iteration is skipped by virtue of the continue
statement.
Recall that continue
effectively skips any code that comes after it and continues on execution with the next iteration. In the code above, when continue
is encountered, all the logic from lines 16 - 18 is ignored and the loop runs again from line 6, thus making the desired input prompt again.
Although this code does the same job as the previous one, it is slightly less readable.
As we stated before, continue
and break
tend to introduce holes into the reading flow of a program — we can't really be sure whether any following code will be executed until and unless we go through the entire loop containing these control-flow keywords.
So whenever and wherever possible, we must resort to using basic programming constructs to achieve a given task in a loop rather than using the commands continue
and break
.