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
:
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:
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.
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 theswitch
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 ourcase
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 theswitch
statement and likewise prevent execution from entering into the next case.
Now, let's try running the code:
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.
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';
}
The if
checks for the first two cases while else if
checks for the rest two of them.
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.
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);
Notice how we don't have to use break
in each case clause. That's simply because return
is doing the exact same thing.
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 return
s 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);
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);
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:
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';
}
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.