Asked  7 Months ago    Answers:  5   Viewed   51 times

I saw this today in some PHP code:

$items = $items ?: $this->_handle->result('next', $this->_result, $this);

I'm not familiar with the ?: operator being used here. It looks like a ternary operator, but the expression to evaluate to if the predicate is true has been omitted. What does it mean?

 Answers

88

It evaluates to the left operand if the left operand is truthy, and the right operand otherwise.

In pseudocode,

foo = bar ?: baz;

roughly resolves to

foo = bar ? bar : baz;

or

if (bar) {
    foo = bar;
} else {
    foo = baz;
}

with the difference that bar will only be evaluated once.

You can also use this to do a "self-check" of foo as demonstrated in the code example you posted:

foo = foo ?: bar;

This will assign bar to foo if foo is null or falsey, else it will leave foo unchanged.

Some more examples:

<?php
    var_dump(5 ?: 0); // 5
    var_dump(false ?: 0); // 0
    var_dump(null ?: 'foo'); // 'foo'
    var_dump(true ?: 123); // true
    var_dump('rock' ?: 'roll'); // 'rock'
?>

By the way, it's called the Elvis operator.

Elvis operator

Wednesday, March 31, 2021
 
van_folmert
answered 7 Months ago
70

The basics:

  1. An assignment expression results in the assigned value.

    What does that mean? $foo = 'bar' is an expression, in which the assignment operator = assigns a value. An expression always returns a value itself. Just like the expression 1 + 2 results in the value 3, the expression $foo = 'bar' results in the value 'bar'. That's why this works:

    $foo = $bar = 'baz'; // which is: $foo = ($bar = 'baz');
    
  2. Boolean operations are short-circuiting operations. Both sides are not always evaluated if they don't need to be. true || false is always true overall, since the lefthand operand is true, so the whole expression must be true. false is not even being evaluated here.

  3. Operator precedence dictates in which order parts of an expression are grouped into sub-expressions. Higher precedence operators are grouped with their operands before lower precedence operators.

Therefore:

$e = false || true;

false || true is being evaluated, which results in the value true, which is assigned to $e. The || operator has a higher precedence than =, therefore false || true is grouped into an expression (as opposed to ($e = false) || true).

$f = false or true;

Here now or has a lower precedence than =, which means the assignment operation is grouped into one expression before or. So first the $f = false expression is evaluated, the result of which is false (see above). So then you have the simple expression false or true which is evaluated next and results in true, but which nobody cares about.

The evaluation works like this:

1. $f = false or true;
2. ($f = false) or true;  // precedence grouping
3. false or true;         // evaluation of left side ($f is now false)
4. true;                  // result

Now:

$foo or $foo = 5; 

Here, again, $foo = 5 has a higher precedence and is treated as one expression. Since it occurs on the right side of the or operator, the expression is only evaluated if necessary. It depends on what $foo is initially. If $foo is true, the right hand side will not be evaluated at all, since true or ($foo = 5) must be true overall. If $foo has a falsey value initially though, the right hand side is evaluated and 5 is assigned to $foo, which results in 5, which is true-ish, which means the overall expression is true, which nobody cares about.

1. $foo or $foo = 5;
2. $foo or ($foo = 5);   // precedence grouping
3. false or ($foo = 5);  // evaluation of left side
4. false or 5;           // evaluation of right side ($foo is now 5)
5. true;                 // result
Wednesday, March 31, 2021
 
jeremyharris
answered 7 Months ago
39

In PHP, the conditional operator is left-associative[PHP.net], compared to virtually all other languages where it is right-associative.

That's why you need to use parentheses to control the order of evaluation1:

 condition  ? code_if_true  : 
(condition2 ? code_if_true2 : 
              code_if_false ); 

1The order in which which operators are resolved, not when operands are evaluated. The latter is basically undefined[PHP.net]

Saturday, May 29, 2021
 
Arman
answered 5 Months ago
36

It is a unary operator (taking a single argument) that is borrowed from C, where all data types are just different ways of interpreting bytes. It is the "invert" or "complement" operation, in which all the bits of the input data are reversed.

In Python, for integers, the bits of the twos-complement representation of the integer are reversed (as in b <- b XOR 1 for each individual bit), and the result interpreted again as a twos-complement integer. So for integers, ~x is equivalent to (-x) - 1.

The reified form of the ~ operator is provided as operator.invert. To support this operator in your own class, give it an __invert__(self) method.

>>> import operator
>>> class Foo:
...   def __invert__(self):
...     print 'invert'
...
>>> x = Foo()
>>> operator.invert(x)
invert
>>> ~x
invert

Any class in which it is meaningful to have a "complement" or "inverse" of an instance that is also an instance of the same class is a possible candidate for the invert operator. However, operator overloading can lead to confusion if misused, so be sure that it really makes sense to do so before supplying an __invert__ method to your class. (Note that byte-strings [ex: 'xff'] do not support this operator, even though it is meaningful to invert all the bits of a byte-string.)

Tuesday, June 1, 2021
 
jedwards
answered 5 Months ago
23

?: is a form of the conditional operator which was previously available only as:

expr ? val_if_true : val_if_false

In 5.3 it's possible to leave out the middle part, e.g. expr ?: val_if_false which is equivalent to:

expr ? expr : val_if_false

From the manual:

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

Wednesday, June 9, 2021
 
Sendy
answered 5 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :