- URL: https://www.laruence.com/en/2020/07/09/6015.html
- Please include attribution when republishing.
This originally came from someone on Weibo who said he often uses it to interview candidates. Roughly: for the following code, what do you think gets printed?
$a = true;
if ($a) {
echo "true";
} else label: {
echo "false";
}
At the time it felt too niche to write about; at lunch today someone asked me again, so let me explain the reason.
First, the code above outputs truefalse. If you already know why, you can stop reading; if not, then:
The confusion likely comes from our intuition that
label : {
statement;
}
should form a single unit, just like:
if ($a) {
} else switch($a) {
}
or:
if ($a) {
} else do {
} while (!$a);
Because in PHP's grammar design, if else is essentially:
if_stmt: if_stmt_without_else T_ELSE statement
That is, anything that is a statement may follow else; when the condition fails, control flow jumps to the statement after else — and while and switch both reduce to statement.
But the label part is a bit special (arguably a counter-intuitive "flaw" in the design). In zend_language_parser.y:
statement:
...
| T_DO statement T_WHILE '(' expr ')' ';' {...}
| T_SWITCH '(' expr ')' switch_case_list {...}
| T_STRING ‘:’ { $$ = zend_ast_create(ZEND_AST_LABEL, $1); }
As you can see, do while and switch each reduce to a statement together with their bodies — but a label is different: "label:" by itself reduces to a statement. That produces this seemingly baffling behavior; the code essentially becomes:
$a = true;
if ($a) {
echo "true";
} else {
label: ; // a single standalone statement
}
echo "false";
One last side note — I forget where I read it, but supposedly there is no such thing as elseif in this world; there is only else (if statement). Essentially the same idea: a statement may follow else.
Used well with switch, for, do while and friends, this can sometimes make our code more compact.
For example, to iterate over an array and do something else when it's empty, many people would write:
if (count($array)) {
for ($i = 0; $i < count($array); $i++) {
}
} else {
// empty-array logic
}
But you could also write:
if (count($array) == 0) {
// empty-array logic
} else for ($i = 0; $i < count($array); $i++) {
}
Which of the two styles is better is a matter of taste.
Finally, if you run into similar puzzling problems in practice, leave a comment — maybe it becomes a post of its own someday.
Be First to Comment