note 98225 deleted from control-structures.switch by crell
| From: | crell@php.net | Date: | Mon, 27 Jun 2022 21:33:10 +0000 |
| Subject: | note 98225 deleted from control-structures.switch by crell | ||
| References: | 1 | Groups: | php.notes |
| Request: | Send a blank email to php-notes+get-218646@lists.php.net to get a copy of this message | ||
Note Submitter: theimp at iinet dot net dot au
----
It's easy to abuse the switch syntax to do some very useful things. As this example will show,
the possibilities can go beyond even Duff's Device-style craziness (not that this example is
nearly as clever as Duff's Device, but it demonstrates how you can do certain things other than
simply the increment/decrement/assignment that's possible in C).
Fundamentally, this works mostly due to the fact that, from the point of view of the
assembler/interpreter, a switch block is hardly any different from a bunch of GOTO labels and if()
evaluations. But, like an if() evaluation, the line of a case: statement is evaluated as an
expression. So, in this case, we can perform an assignment and match the result of that assignment,
because the return value of an assignment is the data that was assigned (and not the value of the
variable it was assigned to, as you might expect).
So far, this is not actually amazing, even if it is a bit unintuitive. From a language
point-of-view, it would be the same as an if($var = "string") statement which is using
an assignment (=) rather than a comparison (==) operator. When you look at the pre-processing
optimization, because a normal assignment of $var = "string" will always equal
"string", it makes sense to have the result of that expression simply be equal to the
right side of the expression (the right side is used rather than the left to let the
assembler/interpreter work faster, on account of how they traditionally simply change the memory
location for the assigned variable rather than copy the memory around unnecessarily).
Where this becomes more interesting is where, in PHP, you have language constructs that behave like
functions but are used like statements. An $array[] = "string" expression is actually a
language construct that just happens to behave a lot like a function, but you use it in the same way
that you use an assignment expression, and like an expression, it always evaluates to the right side
of the expression; in this case, "string" and not array() .
The assembler/interpreter can't use the right side of the expression as a shortcut for the
result of a function, so you can't use functions in this way in a case statement. You also
can't get around this limit on calling functions from the case line by using variable
functions, because they are used in the same way as functions.
But imagine what you could do with other language constructs, like eval() or include() !
Consider the following:
<?php
function flavor($type = null)
{
switch ($type) {
/* Note the isolation of break; statements and the fact that default: is at the top */
default:
$type = null;
case $array[] = "chocolate":
if ($type != null) {
$array = array($type);
break;
}
case $array[] = "strawberry":
if ($type != null) {
$array = array($type);
break;
}
case $array[] = "vanilla":
if ($type != null) {
$array = array($type);
break;
}
}
if ( (count($array) != 1) ) {
return "Flavors available: " . implode(", ", $array);
} else {
return "Flavor selected: " . implode(", ", $array);
}
}
echo flavor() . "<br>";
/* Flavors available: chocolate, strawberry, vanilla */
echo flavor("banana") . "<br>";
/* Flavors available: chocolate, strawberry, vanilla */
echo flavor("chocolate") . "<br>";
/* Flavor selected: chocolate */
?>
What makes this example useful is that you don't need a variable somewhere that contains the
available options (even within the function itself), so to support new options, you only ever have
to change the code to add the new option - you don't need to update some variable somewhere
that controls whether or not it works or whether or not people can tell that there's a new
option.