20

I dont understand how that output ("four") comes?

$a = 2;

echo
  $a == 1 ? 'one'   :
  $a == 2 ? 'two'   :
  $a == 3 ? 'three' :
  $a == 5 ? 'four'  : 
    'other'  
  ;

// prints 'four'

I don't understand why "four" gets printed.

hakre
  • 193,403
  • 52
  • 435
  • 836
Jimit
  • 2,201
  • 7
  • 32
  • 66
  • It will be printed for $a = 5; – Rikesh Jun 03 '11 at 07:45
  • 1
    @riky: yes, but `$a=2` in his code... – Marco Jun 03 '11 at 07:46
  • @riky @Marco, can anyone tell me how parser does parse that code? – Jimit Jun 03 '11 at 07:48
  • 1
    YA so according to your login it will print "two". Can you explain me what exactly you want? – Rikesh Jun 03 '11 at 07:48
  • Syntax `cond ? true_part : false_part` is the same as `if (cond == true) true_part else false_part`. So your code should print two. Strange... – Marco Jun 03 '11 at 07:49
  • @riky you misunderstand. That statement is not written correctly so it will print 'four'. The OP wants to know why it does this. – JohnP Jun 03 '11 at 07:50
  • @riky I want to know How this code will be parsed by parser? – Jimit Jun 03 '11 at 07:50
  • @Jimit : May be 2 is not assigned to 'a' properly. Try to print he value of 'a' – Stuti Jun 03 '11 at 07:50
  • @JohnP Yes, right John . – Jimit Jun 03 '11 at 07:51
  • 4
    == operator has grater precedence level than ternar operator. So you must use brackets to group. http://php.net/manual/en/language.operators.precedence.php – Matej Baćo Jun 03 '11 at 07:54
  • @MatejB Add that as an answer. It's the only correct answer to this question. – JohnP Jun 03 '11 at 08:00
  • @MatejB, I know I should use brackets man! but I want to know how that hell output being generated by parser? – Jimit Jun 03 '11 at 08:00
  • 3
    Apart from being completely unreadable, you should avoid stacking ternary operators. See the note in the manual http://de2.php.net/manual/en/language.operators.comparison.php – Gordon Jun 03 '11 at 08:01
  • Were any of the answers below acceptable? – MGwynne Jul 17 '12 at 10:26
  • 1
    In C/C++/Java you would get "two". The actual reason why PHP differs is because PHP handles `?:` associativity from left to right, instead of right to left. This is a well know PHP misdesign. See [this explanation](http://www.php.net/manual/en/language.operators.precedence.php#91377) and [that criticism](http://me.veekun.com/blog/2012/04/09/php-a-fractal-of-bad-design/#operators). This question is not off topic as this is a real and problematic PHP precedence issue. – Déjà vu Jan 25 '14 at 07:58
  • It's important to note that with PHP 7.4, using of nested ternary conditions is deprecated, and you have to use parenthesis: see https://wiki.php.net/rfc/ternary_associativity and https://3v4l.org/K0Qr8 – Michele Locati Nov 12 '19 at 07:36

4 Answers4

32

You need to bracket the ternary conditionals:

<?php

for ($a=0; $a < 7; $a++) {
  echo (
    $a == 1 ? 'one' :
    ($a == 2 ? 'two' :
    ($a == 3 ? 'three' :
    ($a == 5 ? 'four' : 'other'))));
    echo "\n";
    // prints 'four'
}
exit;
?>

returns:

other
one
two
three
other
four
other

as you'd expect.

See the note at the bottom of "Ternary operators" at PHP Ternary operator help.

The expressions are being evaluated left to right. So you are actually getting:

  echo (
    ((($a == 1 ? 'one' : $a == 2)
     ? 'two' : $a == 3) ? 'three' :
    $a == 5) ? 'four' : 'other');

So for $a=2, you get:

  echo (
    ((($a==2) ? 'two' : $a == 3) ? 'three' :
    $a == 5) ? 'four' : 'other');

and then

  echo (
    ((true ? 'two' : $a == 3) ? 'three' :
    $a == 5) ? 'four' : 'other');

and then

  echo (
    ('two' ? 'three' : $a == 5) ? 'four' : 'other');

and then

  echo (
    'three' ? 'four' : 'other');

and so echo 'four'.

Remember that PHP is dynamically typed and treats any non-zero non-null values as TRUE.

MGwynne
  • 3,512
  • 1
  • 23
  • 35
  • 4
    Because == operator has grater precedence level than ternar operator and hell breaks lose ;) – Matej Baćo Jun 03 '11 at 07:56
  • err, you beat me with nearly 3 minutes but the system didn't even notice me, grrr. –  Jun 03 '11 at 07:56
  • 1
    awesome explaination.. even i was not getting how answer this snippet. thanks dude.. – Jaimin Jun 03 '11 at 09:06
  • 3
    This is completely opposite behavior comparing to ternary operator in other languages, e.g: C/C++ or Java. Even JavaScript does it in the right (expected) way. Anyway, as mentioned in PHP manual, it's not recommended to use nested ternary expressions – biera Feb 14 '14 at 16:42
6

On the Comparison Operators page in the PHP Manual they explain that PHP's behavior is "non-obvious" when nesting (stacking) ternary operators.

The code you've written is like this:

$a = 2;

echo
  ((($a == 1  ? 'one'   :
     $a == 2) ? 'two'   :
     $a == 3) ? 'three' :
     $a == 5) ? 'four'  : 
       'other'
  ;

// prints 'four'

As $a is 2 and both 'two' and 'three' are TRUE as well, you get "four" as the result, as you don't compare any longer if 'four' is TRUE or not.

If you want to change that, you have to put the brackets at different places [also noted by: BeingSimpler and MGwynne]:

$a = 2;
echo 
  ($a == 1 ? 'one'   :
  ($a == 2 ? 'two'   :
  ($a == 3 ? 'three' :
  ($a == 5 ? 'four'  : 
     'other'))))
  ;

// prints 'two'
hakre
  • 193,403
  • 52
  • 435
  • 836
Samuel
  • 18,286
  • 18
  • 52
  • 88
  • @BeingSimpler: Couldn't find the link ;) Don't worry I didn't copy your code :) – Samuel Jun 03 '11 at 07:59
  • I know you didn't, the system delayed a little bit. My answer above is actually 3 minutes later than MGwynne's answer, but when I posted it appeared the same time. –  Jun 03 '11 at 08:01
3

Problem with grouping conditions, just need to add brackets to separate them.

$a = 2;
echo (
$a == 1 ? 'one' :
($a == 2 ? 'two' :
($a == 3 ? 'three' :
($a == 5 ? 'four' : 'other'))));
echo "\n";
// prints 'four'
exit;

Solved.

2

This is what I came up with to help myself understand left vs. right associativity for the ternary operator.

// PHP

$a = "T";
$vehicle =  $a == "B" ? "bus" :
            $a == "A" ? "airplane" :
            $a == "T" ? "train" :
            $a == "C" ? "car" :
            $a == "H" ? "horse" : "feet";

            // (as seen by the PHP interpreter)
            // INITIAL EXPRESSION: ((((($a == "B" ? "bus" : $a == "A") ? "airplane" : $a == "T") ? "train" : $a == "C") ? "car" : $a == "H") ? "horse" : "feet");
            // STEP 1:             (((((FALSE ? "bus" : FALSE) ? "airplane" : TRUE) ? "train" : FALSE) ? "car" : FALSE) ? "horse" : "feet")
            // STEP 2:             ((((FALSE ? "airplane" : TRUE) ? "train" : FALSE) ? "car" : FALSE) ? "horse" : "feet")
            // STEP 3:             (((TRUE ? "train" : FALSE) ? "car" : FALSE) ? "horse" : "feet")
            // STEP 4:             (("train" ? "car" : FALSE) ? "horse" : "feet")
            // STEP 5:             ("car" ? "horse" : "feet")
            // FINAL EVALUATION:   ("horse")

            // If you used the initial expression here (with the parenthesis) in a different language, it would also evaluate to "horse."

echo $vehicle; // gives us "horse"

This is as opposed to:

// EVERY OTHER LANGUAGE

var a = "T";
var vehicle =   a == "B" ? "bus" :
                a == "A" ? "airplane" :
                a == "T" ? "train" :
                a == "C" ? "car" :
                a == "H" ? "horse" : "feet";

                // (as seen by the other language's interpreter)
                // INITIAL EXPRESSION: (a == "B" ? "bus" : (a == "A" ? "airplane" : (a == "T" ? "train" : (a == "C" ? "car" : (a == "H" ? "horse" : "feet")))));
                // STEP 1:             (FALSE ? "bus" : (FALSE ? "airplane" : (TRUE ? "train" : (FALSE ? "car" : (FALSE ? "horse" : "feet")))))
                // STEP 2:             (FALSE ? "bus" : (FALSE ? "airplane" : (TRUE ? "train" : (FALSE ? "car" : "feet"))))
                // STEP 3:             (FALSE ? "bus" : (FALSE ? "airplane" : (TRUE ? "train" : "feet")))
                // STEP 4:             (FALSE ? "bus" : (FALSE ? "airplane" : "train"))
                // STEP 5:             (FALSE ? "bus" : "train")
                // FINAL EVALUATION:   ("train")

                // If you used the initial expression here (with the parenthesis) in PHP, it would also evaluate to "train."

console.log(vehicle); // gives us "train"

If you notice, in the PHP example, the innermost expression is on the left, and on the second example, the innermost expression is on the right. Each step evaluates the next innermost expression until there is a single result. Parenthesis are clearly very important if you're going to nest ternary operations in PHP!

Jonathon
  • 91
  • 1
  • 2