Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In PHP, why does "or die()" work, but "or return" doesn't?

Tags:

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?

like image 866
Sosumi Avatar asked Jan 18 '13 05:01

Sosumi


1 Answers

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.

like image 193
AgentConundrum Avatar answered Sep 27 '22 18:09

AgentConundrum