In PHP, you can handle errors by calling or die
to exit when you encounter certain errors, like this:
$handle = fopen($location, "r") or die("Couldn't get handle");
Using die()
isn't a great way to handle errors. I'd rather return an error code so the parent function can decide what to do, instead of just ending the script ungracefully and displaying the error to the user.
However, PHP shows an error when I try to replace or die
with or return
, like this:
$handle = fopen($location, "r") or return 0;
Why does or die()
work, but not or return 0
?
I want to thank you for asking this question, since I had no idea that you couldn't perform an or return
in PHP. I was as surprised as you when I tested it. This question gave me a good excuse to do some research and play around in PHP's internals, which was actually quite fun. However, I'm not an expert on PHP's internals, so the following is a layman's view of the PHP internals, although I think it's fairly accurate.
or return
doesn't work because return
isn't considered an "expression" by the language parser - simple as that.
The keyword or
is defined in the PHP language as a token called T_LOGICAL_OR
, and the only expression where it seems to be defined looks like this:
expr T_LOGICAL_OR { zend_do_boolean_or_begin(&$1, &$2 TSRMLS_CC); } expr { zend_do_boolean_or_end(&$$, &$1, &$4, &$2 TSRMLS_CC); }
Don't worry about the bits in the braces - that just defines how the actual "or" logic is handled. What you're left with is expr T_LOGICAL_OR expr
, which just says that it's a valid expression to have an expression, followed by the T_LOGICAL_OR
token, followed by another expression.
An expr
is also defined by the parser, as you would expect. It can either be a r_variable
, which just means that it's a variable that you're allowed to read, or an expr_without_variable
, which is a fancy way of saying that an expression can be made of other expressions.
You can do or die()
because the language construct die
(not a function!) and its alias exit
are both represented by the token T_EXIT
, and T_EXIT
is considered a valid expr_without_variable
, whereas the return
statement - token T_RETURN
- is not.
Now, why is T_EXIT
considered an expression but T_RETURN
is not? Honestly, I have no clue. Maybe it was just a design choice made just to allow the or die()
construct that you're asking about. The fact that it used to be so widely used - at least in things like tutorials, since I can't speak to a large volume of production code - seems to imply that this may have been an intentional choice. You would have to ask the language developers to know for sure.
With all of that said, this shouldn't matter. While the or die()
construct seemed ubiquitous in tutorials (see above) a few years ago, it's not really recommended, since it's an example of "clever code". or die()
isn't a construct of its own, but rather it's a trick which uses - some might say abuses - two side-effects of the or
operator:
- it is very low in the operator precedence list, which means practically every other expression will be evaluated before it is
- it is a short-circuiting operator, which means that the second operand (the bit after the
or
) is not executed if the first operand returns TRUE
, since if one operand is TRUE
in an or
expression, then they both are.
Some people consider this sort of trickery to be unfavourable, since it is harder for a programmer to read yet only saves a few characters of space in the source code. Since programmer time is expensive, and disk space is cheap, you can see why people don't like this.
Instead, you should be explicit with your intent by expanding your code into a full-fledged if
statement:
$handle = fopen($location, "r");
if ($handle) {
// process the file
} else {
return 0;
}
You can even do the variable assignment right in the if
statement. Some people still find this unreadable, but most people (myself included) disagree:
if ($handle = fopen($location, "r")) {
// process the file
} else {
return 0;
}
One last thing: it is convention that returning 0
as a status code indicates success, so you would probably want to return a different value to indicate that you couldn't open the file.
I've also stumbled upon that once. All I could find was this:
https://bugs.php.net/bug.php?id=40712
Look at the comment down below:
this is not a bug
I've searched in the documentation and I think it's due to the fact that return 0
is a statement whereas die()
is essentially an expression. You can't run $handle = return 0;
but $handle = fun();
is valid code.
Regarding error handling I would recommend custom codes or using custom handlers and triggers. The latter are described here for example.
Return is fairly special - it cannot be anything like a function since it's a tool to exit functions. Imagine this:
if(1==1) return(); // say what??
If it was like this, return would have to be a function that does a "double exit", leaving not just its own scope but the caller's, too. Therefore return is nothing like an expression, it simply can't work that way.
Now in theory, return could be an expression that evaluates to (say) false and then quits the function; maybe a later php version will implement this.
The same thing applies to goto which would be a charm to work as a fallback; and yes, fallbacks are necessary and often make the code readable, so if someone complains about "clever code" (which certainly is a good point) maybe php should have some "official" way to do such a thing:
connectMyDB() fallback return false;
Something like try...catch, just more to the point. And personally, I'd be a lot happier with "or" doing this job since it's working well with English grammar: "connect or report failure".
TLDR: you're absolutely right: return, goto, break - none of them works. Easy to understand why but still annoying.