PHP switch Statement

Chapter 23 29 mins

Learning outcomes:

  1. The idea behind switch
  2. What is switch and its syntax
  3. What is fallthrough
  4. The default keyword
  5. When not to use switch
  6. Things to note while using switch

Idea behind switch

Sometimes while programming, you'll come across a case where you need to test the same thing for different values of the same type over and over again, and perform different actions on each outcome.

Following is an example of such a case:

<?php

$rating = 1;

if ($rating === 1) {
   echo 'Very bad';
}
else if ($rating === 2) {
   echo 'Bad';
}
else if ($rating === 3) {
   echo 'Average';
}
else if ($rating === 4) {
   echo 'Good';
}
else if ($rating === 5) {
   echo 'Excellent';
}

There is a variable $rating that holds a number, and then a whole list of if and else if statements to make different logs based on the value of $rating. The program simply converts a star rating to a description of that rating.

Notice the type of each value that we compare with $rating — every single value is a number, and so we are only checking for different values within the same kind of data type in each if.

Obviously when we run the code above, we get the following log since $rating is 1:

Very bad

The code works absolutely fine, however there is a substantial amount of repetition in it.

See how we have to write $rating === again and again in all the statements in addition to having to write else if a couple of times as well.

Fortunately, there is a much better construct to handle such a case pretty nicely — the switch statement. Let's see how it works.

What is switch?

The switch statement is yet another control-flow statement in PHP in addition to many others.

Defining it precisely:

The switch statement is a conditional statement that literally switches over given blocks of code depending on whether they meet a given criterion.

The basic syntax of a switch statement is quite different from that of an if statement. However, it's still simple to understand.

Here's the minimal syntax for switch:

<?php

switch (expression) {
   case match_1:
      statements;
   case match_2:
      statements;
   /* ... */
   case match_n:
      statements;
}

We start off with the switch keyword, followed by a pair of parentheses (()) containing the expression expression that we ought to test in the statement.

Next comes a pair of curly braces ({}), denoting the body of the switch statement. This is formally referred to as the case block.

This pair of curly braces, unlike in if, else, for and while, does NOT denote a block statement. It's purely a part of the syntax of switch.

The case block is comprised of one or possibly more case statements. Each of these case statements is formally referred to as a case clause.

A case clause simply denotes one possible case that expression could be equal to. Clearly, since a given expression could be equal to one of many values, we could include many case clauses for a switch statement.

A clause begins with the case keyword, followed by the expression (match_1, match_2, ..., match_n) to match the main expression against. Note that this matching is done using the same algorithm that's used by the identity operator (===) in PHP. Then comes a colon (:), followed by a set of statements for the clause.

In the snippet above, we've shown this set of statements as a single statements placeholder, but keep in mind that it actually means that we can have a whole bunch of statements there (without having to use a block statement ({}).

Moving on, where a new case keyword appears, the previous case clause (if there is any) ends.

And that's pretty much it.

Let's now consider an example to help us understand this syntax even better.

A simple example

Remember the star rating program we created above using a bunch of if and else if commands? Let's now rewrite it using switch.

But before we do that, we ought to clarify ourselves on one thing regarding the switch statement — the idea of 'breaking out' of a case clause.

Breaking out of a case clause

By default, when case clauses are matched against the main expression (specified next to the switch keyword), if a case matches, its corresponding set of statements, as is expected, is executed.

But then the next case clause is also executed, and then the next one as well, and then the next one, and so on and so forth.

You might think that this is counter-intuitive but it's not. We'll explain exactly how is this the case later on in the section below.

For now, you should know that to override this default behavior, we can use the break keyword right at the end of each clause's set of statements. It literally helps us 'break out' of the current case clause and, in fact, the entire switch statement.

Back to the star rating program.

First, let's decide what we need to test. What do you think? Well, we need to test the $rating variable. Step one done.

Next, let's see if we can enumerate all the possible values of $rating. Well, we surely could. It can be either 1, 2, 3, 4 or 5. Step two done.

And we're done!

We've successfully determined the two main things required in a switch statement, i.e. the value to test and the possible cases for it to be equal to. Likewise, now we can finally get to the actual coding.

Here's the star rating program rewritten using switch:

<?php

$rating = 1;

switch ($rating) {
   case 1:
      echo 'Very bad';
      break;
   case 2:
      echo 'Bad';
      break;
   case 3:
      echo 'Average';
      break;
   case 4:
      echo 'Good';
      break;
   case 5:
      echo 'Excellent';
      break;
}

Let's understand what's going on here:

  • As determined before, we ought to test the $rating variable. Hence, it goes into the pair of parentheses (()) next to the switch keyword.
  • Inside the case block, we lay out all the cases that $rating could possibly be equal to. Once again, as determined before, there were a total of five cases, and that's exactly the total number of our case clauses.
  • For each of these cases, we have a corresponding set of statements to execute if $rating matches that case.
  • Notice how each set of statements ends with break, simply to exit out of the switch statement and likewise prevent execution from entering into the next case.

Now, let's try running the code:

Very bad

Exactly the same output as before. Wonderful!

As a rule of thumb, remember that it's solving scenarios similar to this star rating program that is the de facto application of switch.

In particular, when we want to compare a variable against numerous values, all of the same type, and then execute a piece of code for each of them, switch is the go-to construct to employ.

What are fallthroughs?

In the previous section, we saw the break command being used inside every single case clause, in order to prevent execution from tipping over into the next clause. Now, we shall understand more about this behavior.

By default, when PHP is done executing a case clause (which means that it matched the main expression), it continues to the next clause, and then to the next one, and so on, until it's explicitly instructed to exit out or it reaches the very end of the switch statement.

This behavior of continuing execution to the next case clause is commonly referred to as fallthrough. Executions literally 'fall through' to the next case clause, hence the name.

At first glance, fallthrough might seem totally counter-intuitive. However, it ain't. In fact, it's a pretty desirable thing.

But how? Well, that can best be understood with the help of an example.

Suppose we have a variable $num and want to see its remainder when divided by 4. If the remainder is 0 or 1, we must log 'First two', or otherwise if it's 2 or 3, we must log 'Last two'.

Now going with our old approach to laying out switch, we'll get the following code:

<?php

$num = 5;

switch ($num % 4) {
   case 0:
      echo 'First two';
      break;
   case 1:
      echo 'First two';
      break;
   case 2:
      echo 'Last two';
      break;
   case 3:
      echo 'Last two';
      break;
}

Each case clause is clearly showcased and the code indeed does what it's asked to do.

First two

However, the code can't be classified as one of the best code snippets out there. There clearly is a little bit of undesirable repetition in it.

Technically, the outcome of both case 0 and case 1 is the exact same and so we shouldn't ideally be repeating the exact same echo statement in both of them. The same goes for the last two cases.

At least for now, since we only need to log the value, the repetition might not seem like an issue. But imagine if there were two different blocks of statements, over 100 lines, that we ought to execute depending on the scenario.

Would it then be sensible to repeat the whole code again? Clearly not.

To address cases like these, the switch statement was designed to take on the behavior of fallthrough.

A case clause can be written without a corresponding set of statements, and in that case, if it matches the main expression of the switch statement, execution falls through to the next case clause.

Fallthrough is more or less analogous to the logical OR (||) operator used in an if's condition.

For example, if we rewrite the code above using if and ||, here's what we get:

<?php

$num = 5;

if ($num % 4 === 0 || $num % 4 === 1) {
   echo 'First two';
}
else if ($num % 4 === 2 || $num % 4 === 3) {
   echo 'Last two';
}
First two

The if checks for the first two cases while else if checks for the rest two of them.

We could've just used else here instead of else if, but we didn't in order to showcase the exact mapping of the last two case clauses to a corresponding else if.

Notice how there are just two log statements, instead of four. This is good because we have prevented that repetition.

But now there is another kind of repetition which is in the expression that we ought to compare, i.e. $num % 4. It could very easily be prevented by creating a separate variable to hold the result of $num % 4 and then using the variable in the comparisons instead.

Obviously, however, since we're using if, we can do much better if we instead use switch.

So, let's now rewrite the code above using switch, leveraging its fallthrough mechanism:

<?php

$num = 5;

switch ($num % 4) {
   case 0:
   case 1:
      echo 'First two';
      break;
   case 2:
   case 3:
      echo 'Last two';
      break;
}

See how we've stripped off the set of statements from the first case clause and the third one. Syntactically, this code says that "if the variable $num has a remainder of 0 or 1, log 'First two', or otherwise if it has a remainder of 2 or 3, log 'Last two'."

If the fallthrough behavior is not desired, break could be used. The break statement effectively cuts off the fallthrough mechanism and literally breaks out of a switch statement.

For instance, the code below won't work:

<?php

$num = 4;

switch ($num % 4) {
   case 0:
      // Because of the following break, the code won't work!
break; case 1: echo 'First two'; break; case 2: case 3: echo 'Last two'; break; }

We've changed the value of $num to 4 so that the $num % 4 matches the first switch case. Since the first case has break in it, execution immediately terminates as soon as it encounters it.

The result is that there is nothing logged in the console.

We always have to be careful with such small kinds of details, because, as in the case above, they can lead to unexpected outcomes.

Moving on, keep in mind that being able to break out of a switch statement isn't just limited to break; we can do the exact same thing using return as well, but we ought to make sure that it's used only inside a function.

Using return outside a function is illegal!

In the code below, we restructure our star rating program with the help of a function getRatingDescription() which takes in a given rating and returns back its description:

<?php

function getRatingDescription($rating) {
   switch ($rating) {
      case 1:
         return 'Very bad';
      case 2:
         return 'Bad';
      case 3:
         return 'Average';
      case 4:
         return 'Good';
      case 5:
         return 'Excellent';
   }
}

$rating = 1;
echo getRatingDescription($rating);
Very bad

Notice how we don't have to use break in each case clause. That's simply because return is doing the exact same thing.

Precisely, return not just breaks execution out of switch but also out of the entire enclosing function.

If we were to alter this code by removing the returns and storing the description in a local variable, as shown below, it would cease to work properly:

<?php

function getRatingDescription($rating) {
   $description;
   switch ($rating) {
      case 1:
         $description = 'Very bad';
      case 2:
         $description = 'Bad';
      case 3:
         $description = 'Average';
      case 4:
         $description = 'Good';
      case 5:
         $description = 'Excellent';
   }
   return $description;
}

$rating = 1;
echo getRatingDescription($rating);
Excellent

When a case matches, $description does get set to the corresponding string, but then by virtue of fallthrough, the next case clause executes as well, and then the next one, and so on, all the way to the very last case clause, ultimately setting $description to 'Excellent'.

So no matter what value comes into the function as $rating, $description gets assigned the value 'Excellent'. And this is absolutely wrong.

If we want to keep using this variable approach, then we ought to use break after every variable assignment statement in order to prevent fallthrough, as shown below:

<?php

function getRatingDescription($rating) {
   $description;
   switch ($rating) {
      case 1:
         $description = 'Very bad';
         break;
      case 2:
         $description = 'Bad';
         break;
      case 3:
         $description = 'Average';
         break;
      case 4:
         $description = 'Good';
         break;
      case 5:
         $description = 'Excellent';
         break;
   }
   return $description;
}

$rating = 1;
echo getRatingDescription($rating);
Very bad

To boil it all down, whenever using switch make sure to evaluate every case clause and see if fallthrough is really desired, and if not, then whether a break or return is being used to prevent it.

The default clause

Just how we have else to execute a given piece of code when the preceding if doesn't match, so do we have default for switch.

As the name suggests, the default keyword is used to specify the default clause to execute in a switch statement when neither of the given cases matches.

We can also refer to the default clause as the fallback clause. That is, when nothing gets matched, we 'fall back' on to the default clause.

Syntactically, it could be expressed as follows:

<?php

switch (expression) {
   case match_1:
      statements;
   case match_2:
      statements;
   /* ... */
   case match_n:
      statements;
   default:
      statements;
}

Although, we've written default as the last clause, and it makes sense to have the fallback clause in the very end, there's no need to. We could have default anywhere in between the clauses.

Let's consider an example.

Suppose we want to test a variable $char to see if it is a vowel or a consonant. If it is a vowel (a, e, i, o, u), we need to log 'Vowel', or otherwise log 'Consonant'. Quite basic.

Here's how we'll implement this program:

<?php

$char = 'a';

switch ($char) {
   case 'a':
   case 'e':
   case 'i':
   case 'o':
   case 'u':
      echo 'Vowel';
      break;
   default:
      echo 'Consonant';
}

The cases that could be easily enumerated, i.e. the five vowels, are listed as case clauses with their respective log statement, while the rest of the cases, i.e. the consonants, are all represented in one single default clause, again with their respective log statement.

Obviously, when we'll run the program, we'll get 'Vowel' logged, since $char is 'a' — a vowel:

Vowel

Pretty simple, isn't this?

Now, it's time for you to predict the output of a similar piece of code.

What does the following code log?

<?php

$text = 'JavaScript';

switch ($text[1]) {
   case 'a':
   case 'e':
   case 'i':
   case 'o':
   case 'u':
      echo 'Vowel', "\n";
   default:
      echo 'Consonant', "\n";
}
  • Vowel
  • Consonant
  • Vowel Consonant

Remember that the default clause is the last clause that is executed in a switch statement, even if it's mentioned at the start of a switch statement, after all the stated case clauses are inspected.

Likewise, the following code won't magically log 'Default':

<?php

switch ('a') {
   default:
      echo 'Default';
      break;
   case 'a':
      echo 'A';
}
A

As execution enters switch, first of all, case 'a' is inspected, not the default case even though the default case comes first in the source code. Since 'a' is the matching case, the corresponding log 'A' is made.

Besides this, also remember that the fallthrough mechanism works exactly the same way in a default clause as it otherwise would in a normal case clause.

When not to use switch?

At this point in this chapter, we know a couple of things regarding switch: what exactly is switch, how to write it, what is fallthrough, what's the break keyword, and so on and so forth.

We surely have come to know a lot about this new conditional construct of PHP, but there still is another equally important thing that we haven't specifically shed light on.

And that's when to and when not to use switch.

We'll talk about the latter, i.e. when not to use switch, which would automatically tell us about when should we actually use it.

There are just two cases, with one being the default one.

If there are only two cases to address, and one of them is the default case, it would be an overkill to use a switch. Such a problem can easily and more compactly be solved using if and else.

For instance, suppose we want to make different logs depending on whether a given number is even or odd.

Doing this with switch would look something as follows:

<?php

$num = 20;

switch ($num % 2) {
   case 0:
      echo 'Even';
      break;
   default:
      echo 'Odd';
}

Whereas doing the same thing with if and else would look like this:

<?php

$num = 20;

if ($num % 2 === 0) {
   echo 'Even';
}
else {
   echo 'Odd';
}

Which one seems more readable and sensible? The second one, indeed.

Hence, when there are two cases to deal with and one of them is the fallback case, then we're much better off with sticking to the good old if and else.

The comparisons aren't identity comparisons.

As you already know, each case clause's expression is compared with the main expression of the switch statement using the same algorithm used internally by the identity (===) operator.

Now, if we want to lay out conditions using other operators (such as <=, or >=, or !==), then it's NOT sensible to use switch (even though technically it's still possible).

For example, let's suppose we want to check if a given number is greater than 10.

With the if statement, we can accomplish this very easily and sensibly:

<?php

$num = 30;

if ($num > 10) {
   echo 'Greater than 10';
}

But doing the same thing with switch, we'd get the following:

<?php

$num = 30;

switch ($num > 10) {
   case true:
      echo 'Greater than 10';
}

Right at first glance, this piece of code doesn't at all look nice. To make it even worse, let's say we have two conditions to check for.

With if, here's the new code:

<?php

$num = 30;

if ($num > 20) {
   echo 'Greater than 20';
}
else if ($num > 10) {
   echo 'Greater than 10';
}

And now with switch, here's the weird-looking code:

<?php

$num = 30;

switch ($num > 20) {
   case true:
      echo 'Greater than 20';
      break;
   default:
      switch ($num > 10) {
         case true:
            echo 'Greater than 10';
      }
}

This time, it's obviously even more apparent that switch is absolutely NOT meant to be used in cases where we need to lay out conditions with operators other than ===.

Remember that switch is only meant to check whether a given expression evaluates down to a specific value, NOT to check whether it falls within a given range, or whether it is related to some other value in some way, or anything else.