23

For the very common case of assigning a value to a variable based on the outcome of an expression I'm a fan of ternary operators:

$foo = $bar ? $a : b;

However, if $bar is a relatively expensive operation and I want to assign the result of $bar to $foo if the result is truthy, this is inefficient:

$foo = SomeClass::bigQuery() ? SomeClass::bigQuery() : new EmptySet();

One option is:

$foo = ($result = SomeClass::bigQuery()) ? $result : new EmptySet();

But I'd rather not have the extra $result sitting in memory.

The best option I've got is:

$foo = ($foo = SomeClass::bigQuery()) ? $foo : new EmptySet();

Or, without ternary operators:

if(!$foo = SomeClass::bigQuery()) $foo = new EmptySet();

Or, if program flow operators are not your style:

($foo = SomeClass::bigQuery()) || ($foo = new EmptySet());

So many options, non of them really satisfactory. Which would you use, and am I missing something really obvious here?

Jonah
  • 9,991
  • 5
  • 45
  • 79
Hamish
  • 22,860
  • 8
  • 53
  • 67

4 Answers4

40

PHP 5.3 introduced a new syntax to solve exactly this problem:

$x = expensive() ?: $default;

See the documentation:

Since PHP 5.3, it is possible to leave out the middle part of the ternary operator.
Expression expr1 ?: expr3 returns expr1 if expr1 evaluates to TRUE, and expr3 otherwise.

user229044
  • 232,980
  • 40
  • 330
  • 338
  • 1
    I personally avoid PHP 5.3-only syntax, and prefer `$x = expensive(); if (!$x) $x = $default;` – user229044 Dec 01 '10 at 22:22
  • Wow; I was just going to comment with "PHP needs the [GNU extension for ternaries](http://stackoverflow.com/questions/3319075/ternary-conditional-operator-behaviour-when-leaving-one-expression-empty)" – Michael Mrozek Dec 01 '10 at 22:23
  • ah ha - missed that in the manual. I assume that means there isn't a better method for 5.2.x (which I have to support in the medium term)? – Hamish Dec 01 '10 at 22:45
  • @Hamish: see the other two answers ;) . But mark this as the answer anyway, because being less than 5.3 was not specified in the question. – Jonah Dec 01 '10 at 22:47
  • 1
    Too bad PHP didn't go with the Javascript/Actionscript solution: Because a value processed in a conditional is not cast to boolean unless it's combined with `&&` you can do `var x = expensive() || default;` This uses an already-familiar syntax and would benefit PHP in more than just one somewhat esoteric case. – Nicole Dec 02 '10 at 00:01
  • 1
    @Renesis I agree - especially since PHP 'truthiness' testing is so loose - it is almost a bug that boolean operators cast objects to bools! – Hamish Dec 02 '10 at 00:07
  • @DanyCaissy I'm late catching it, but please don't edit "man page" into "main page". That wasn't a typo, learn what [man pages](https://en.wikipedia.org/wiki/Man_page) are. – user229044 Dec 21 '17 at 15:00
  • @meagar Haha I guess you meant manual page? I hadn't seen it shortened that way in 2010 ;) – Dany Caissy Dec 22 '17 at 16:02
8

Can you update SomeClass:bigQuery() to return a new EmptySet() instead of false?

Then you just have

$foo = SomeClass::bigQuery();
Jeff Davis
  • 4,736
  • 4
  • 38
  • 44
  • That would actually be my preference - unfortunately I'm building on top of a library that made this mistake from day-0 and now they have too much stuff hanging off the API to fix it :/ – Hamish Dec 01 '10 at 22:27
1
$foo = SomeClass::bigQuery();
if (!$foo) $foo = new EmptySet();

Revision two, credit @meagar

Jonah
  • 9,991
  • 5
  • 45
  • 79
1

A slight variation of your last option:

$foo = SomeClass::bigQuery() or new EmptySet(); this doesn't actually work, thanks for noticing.

Used often in combination with mySQL code, but seems always forgotten in comparable situations:

$result = mysql_query($sql) or die(mysql_error());

Although personally I prefer one you already mentioned:

if(!$foo = SomeClass::bigQuery())
    $foo = new EmptySet();
ontrack
  • 2,963
  • 2
  • 15
  • 14
  • Actually, that needs to read: `$foo = SomeClass::bigQuery() or $foo = new EmptySet();` which is equivalent to the 3rd option. – Hamish Dec 01 '10 at 23:44