PHP while Loop

Chapter 26 19 mins

Learning outcomes:

  1. What is the while loop meant for
  2. Syntax of while
  3. Basic while examples
  4. The do...while loop
  5. Nested loops

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:

The 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:

Enter a number, -1 to exit> 10 Enter a number, -1 to exit> 20 Enter a number, -1 to exit> 30 Enter a number, -1 to exit> -1 The sum is 60.

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";
}
5 3 1

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.

Recall that an infinite loop is one that runs forever and causes the current program to simply crash!

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"; }
5 3 1

The while loop here can be read as: "while the array has some length, keep removing its last element."

Once again, remember that if the condition 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.

It's invalid to put two semicolons (;) 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:

Enter a number, 'q' to end> 10 Enter a number, 'q' to end> 20 Enter a number, 'q' to end> abc Entered value is invalid. Enter a number, 'q' to end> 10x Entered value is invalid. Enter a number, 'q' to end> 1 Enter a number, 'q' to end> q The sum is 31.

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.

"I created Codeguage to save you from falling into the same learning conundrums that I fell into."

— Bilal Adnan, Founder of Codeguage