Understanding nested PHP ternary operator [duplicate] - php

This question already has answers here:
Stacking Multiple Ternary Operators in PHP
(11 answers)
Closed 2 years ago.
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.

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.

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'

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.

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!

Related

How nested ternary operator in PHP works? [duplicate]

This question already has answers here:
Stacking Multiple Ternary Operators in PHP
(11 answers)
Closed 2 years ago.
In the PHP manual, I find the following 'user contributed note' under "Operators".
Note that in php the ternary operator ?: has a left associativity unlike in C and C++ where it has right associativity.
You cannot write code like this (as you may have accustomed to in C/C++):
<?php
$a = 2;
echo (
$a == 1 ? 'one' :
$a == 2 ? 'two' :
$a == 3 ? 'three' :
$a == 4 ? 'four' : 'other');
echo "\n";
// prints 'four'
I actually try it and it really prints four. However I could not understand the reason behind it and still feel it should print two or other.
Can someone please explain what is happening here and why it is printing 'four'?
In any sane language, the ternary operator is right-associative, such that you would expect your code to be interpreted like this:
$a = 2;
echo ($a == 1 ? 'one' :
($a == 2 ? 'two' :
($a == 3 ? 'three' :
($a == 4 ? 'four' : 'other')))); # prints 'two'
However, the PHP ternary operator is weirdly left-associative, such that your code is actually equivalent to this:
<?php
$a = 2;
echo (((($a == 1 ? 'one' :
$a == 2) ? 'two' :
$a == 3) ? 'three' :
$a == 4) ? 'four' : 'other'); # prints 'four'
In case it still isn't clear, the evaluation goes like this:
echo ((((FALSE ? 'one' :
TRUE) ? 'two' :
$a == 3) ? 'three' :
$a == 4) ? 'four' : 'other');
echo ((( TRUE ? 'two' :
$a == 3) ? 'three' :
$a == 4) ? 'four' : 'other');
echo (( 'two' ? 'three' :
$a == 4) ? 'four' : 'other');
echo ( 'three' ? 'four' : 'other');
echo 'four';
Because your whole expression evaluates as if it was (......) ? 'four' : 'other'. Since the first element is probably something truthy, it gives you 'four'. In saner languages, where ?: has right associativity, the whole expression evaluates as if it was $a == 1 ? 'one' : (......), where if $a is not 1, you go on to test other things.
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!
I could not wrap my head around the example from:
https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/
So I came here and I still could not wrap my head around it, so I had to step through it.
#amadan has the best answer, imo.
This prints horse, not train.
// 0
$arg = 'T';
$vehicle =
$arg == 'B' ? 'bus' :
$arg == 'A' ? 'airplane' :
$arg == 'T' ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 1
$vehicle =
> FALSE ? 'bus' :
$arg == 'A' ? 'airplane' :
$arg == 'T' ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 2
$vehicle =
FALSE ? 'bus' :
> FALSE ? 'airplane' :
$arg == 'T' ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 3
$vehicle =
> (FALSE? 'bus' : FALSE? 'airplane' : TRUE)? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 4
$vehicle =
> true ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 5
$vehicle =
> ('train' : $arg == 'C') ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 6
$vehicle =
> (true ? 'car' : $arg == 'H') ? 'horse' :
'feet' ;
// 7
$vehicle =
> (true) ? 'horse' : 'feet' ;
You can see what left associative means in step 5, if I understand correctly.
If you add parentheses, the problem will be solved. Look at the following example:
Without the parentheses, the grade is always D when the marks are greater than 50 but it works fine for marks <= 49.
In order to make the program work as it should, I have added parentheses. It is very easy to know how many parentheses to enter if it is typed like this.
<?php
$marks_obtained = 65;
$grade = null;
//Use parentheses () otherwise the final grade shown will be wrong.
//Excluding the first line, for each additional line,
//we add a parenthesis at the beginning of each line and a parenthesis at the end of the statement.
echo $grade = $marks_obtained >= 90 && $marks_obtained <= 100 ? "A+"
: ($marks_obtained <= 89 && $marks_obtained >= 80 ? "A"
: ($marks_obtained <= 79 && $marks_obtained >= 70 ? "B"
: ($marks_obtained <= 69 && $marks_obtained >= 60 ? "C"
: ($marks_obtained <= 59 && $marks_obtained >= 50 ? "D"
: "F"))))
?>

Odd behaviour with an inline if within an inline if [duplicate]

This question already has answers here:
Stacking Multiple Ternary Operators in PHP
(11 answers)
Closed 2 years ago.
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.
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.
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'
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.
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!

undefined index error with ternary operator PHP [duplicate]

This question already has answers here:
Stacking Multiple Ternary Operators in PHP
(11 answers)
Closed 2 years ago.
In the PHP manual, I find the following 'user contributed note' under "Operators".
Note that in php the ternary operator ?: has a left associativity unlike in C and C++ where it has right associativity.
You cannot write code like this (as you may have accustomed to in C/C++):
<?php
$a = 2;
echo (
$a == 1 ? 'one' :
$a == 2 ? 'two' :
$a == 3 ? 'three' :
$a == 4 ? 'four' : 'other');
echo "\n";
// prints 'four'
I actually try it and it really prints four. However I could not understand the reason behind it and still feel it should print two or other.
Can someone please explain what is happening here and why it is printing 'four'?
In any sane language, the ternary operator is right-associative, such that you would expect your code to be interpreted like this:
$a = 2;
echo ($a == 1 ? 'one' :
($a == 2 ? 'two' :
($a == 3 ? 'three' :
($a == 4 ? 'four' : 'other')))); # prints 'two'
However, the PHP ternary operator is weirdly left-associative, such that your code is actually equivalent to this:
<?php
$a = 2;
echo (((($a == 1 ? 'one' :
$a == 2) ? 'two' :
$a == 3) ? 'three' :
$a == 4) ? 'four' : 'other'); # prints 'four'
In case it still isn't clear, the evaluation goes like this:
echo ((((FALSE ? 'one' :
TRUE) ? 'two' :
$a == 3) ? 'three' :
$a == 4) ? 'four' : 'other');
echo ((( TRUE ? 'two' :
$a == 3) ? 'three' :
$a == 4) ? 'four' : 'other');
echo (( 'two' ? 'three' :
$a == 4) ? 'four' : 'other');
echo ( 'three' ? 'four' : 'other');
echo 'four';
Because your whole expression evaluates as if it was (......) ? 'four' : 'other'. Since the first element is probably something truthy, it gives you 'four'. In saner languages, where ?: has right associativity, the whole expression evaluates as if it was $a == 1 ? 'one' : (......), where if $a is not 1, you go on to test other things.
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!
I could not wrap my head around the example from:
https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/
So I came here and I still could not wrap my head around it, so I had to step through it.
#amadan has the best answer, imo.
This prints horse, not train.
// 0
$arg = 'T';
$vehicle =
$arg == 'B' ? 'bus' :
$arg == 'A' ? 'airplane' :
$arg == 'T' ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 1
$vehicle =
> FALSE ? 'bus' :
$arg == 'A' ? 'airplane' :
$arg == 'T' ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 2
$vehicle =
FALSE ? 'bus' :
> FALSE ? 'airplane' :
$arg == 'T' ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 3
$vehicle =
> (FALSE? 'bus' : FALSE? 'airplane' : TRUE)? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 4
$vehicle =
> true ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 5
$vehicle =
> ('train' : $arg == 'C') ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 6
$vehicle =
> (true ? 'car' : $arg == 'H') ? 'horse' :
'feet' ;
// 7
$vehicle =
> (true) ? 'horse' : 'feet' ;
You can see what left associative means in step 5, if I understand correctly.
If you add parentheses, the problem will be solved. Look at the following example:
Without the parentheses, the grade is always D when the marks are greater than 50 but it works fine for marks <= 49.
In order to make the program work as it should, I have added parentheses. It is very easy to know how many parentheses to enter if it is typed like this.
<?php
$marks_obtained = 65;
$grade = null;
//Use parentheses () otherwise the final grade shown will be wrong.
//Excluding the first line, for each additional line,
//we add a parenthesis at the beginning of each line and a parenthesis at the end of the statement.
echo $grade = $marks_obtained >= 90 && $marks_obtained <= 100 ? "A+"
: ($marks_obtained <= 89 && $marks_obtained >= 80 ? "A"
: ($marks_obtained <= 79 && $marks_obtained >= 70 ? "B"
: ($marks_obtained <= 69 && $marks_obtained >= 60 ? "C"
: ($marks_obtained <= 59 && $marks_obtained >= 50 ? "D"
: "F"))))
?>

Ternary operator left associativity [duplicate]

This question already has answers here:
Stacking Multiple Ternary Operators in PHP
(11 answers)
Closed 2 years ago.
In the PHP manual, I find the following 'user contributed note' under "Operators".
Note that in php the ternary operator ?: has a left associativity unlike in C and C++ where it has right associativity.
You cannot write code like this (as you may have accustomed to in C/C++):
<?php
$a = 2;
echo (
$a == 1 ? 'one' :
$a == 2 ? 'two' :
$a == 3 ? 'three' :
$a == 4 ? 'four' : 'other');
echo "\n";
// prints 'four'
I actually try it and it really prints four. However I could not understand the reason behind it and still feel it should print two or other.
Can someone please explain what is happening here and why it is printing 'four'?
In any sane language, the ternary operator is right-associative, such that you would expect your code to be interpreted like this:
$a = 2;
echo ($a == 1 ? 'one' :
($a == 2 ? 'two' :
($a == 3 ? 'three' :
($a == 4 ? 'four' : 'other')))); # prints 'two'
However, the PHP ternary operator is weirdly left-associative, such that your code is actually equivalent to this:
<?php
$a = 2;
echo (((($a == 1 ? 'one' :
$a == 2) ? 'two' :
$a == 3) ? 'three' :
$a == 4) ? 'four' : 'other'); # prints 'four'
In case it still isn't clear, the evaluation goes like this:
echo ((((FALSE ? 'one' :
TRUE) ? 'two' :
$a == 3) ? 'three' :
$a == 4) ? 'four' : 'other');
echo ((( TRUE ? 'two' :
$a == 3) ? 'three' :
$a == 4) ? 'four' : 'other');
echo (( 'two' ? 'three' :
$a == 4) ? 'four' : 'other');
echo ( 'three' ? 'four' : 'other');
echo 'four';
Because your whole expression evaluates as if it was (......) ? 'four' : 'other'. Since the first element is probably something truthy, it gives you 'four'. In saner languages, where ?: has right associativity, the whole expression evaluates as if it was $a == 1 ? 'one' : (......), where if $a is not 1, you go on to test other things.
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!
I could not wrap my head around the example from:
https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/
So I came here and I still could not wrap my head around it, so I had to step through it.
#amadan has the best answer, imo.
This prints horse, not train.
// 0
$arg = 'T';
$vehicle =
$arg == 'B' ? 'bus' :
$arg == 'A' ? 'airplane' :
$arg == 'T' ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 1
$vehicle =
> FALSE ? 'bus' :
$arg == 'A' ? 'airplane' :
$arg == 'T' ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 2
$vehicle =
FALSE ? 'bus' :
> FALSE ? 'airplane' :
$arg == 'T' ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 3
$vehicle =
> (FALSE? 'bus' : FALSE? 'airplane' : TRUE)? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 4
$vehicle =
> true ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 5
$vehicle =
> ('train' : $arg == 'C') ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 6
$vehicle =
> (true ? 'car' : $arg == 'H') ? 'horse' :
'feet' ;
// 7
$vehicle =
> (true) ? 'horse' : 'feet' ;
You can see what left associative means in step 5, if I understand correctly.
If you add parentheses, the problem will be solved. Look at the following example:
Without the parentheses, the grade is always D when the marks are greater than 50 but it works fine for marks <= 49.
In order to make the program work as it should, I have added parentheses. It is very easy to know how many parentheses to enter if it is typed like this.
<?php
$marks_obtained = 65;
$grade = null;
//Use parentheses () otherwise the final grade shown will be wrong.
//Excluding the first line, for each additional line,
//we add a parenthesis at the beginning of each line and a parenthesis at the end of the statement.
echo $grade = $marks_obtained >= 90 && $marks_obtained <= 100 ? "A+"
: ($marks_obtained <= 89 && $marks_obtained >= 80 ? "A"
: ($marks_obtained <= 79 && $marks_obtained >= 70 ? "B"
: ($marks_obtained <= 69 && $marks_obtained >= 60 ? "C"
: ($marks_obtained <= 59 && $marks_obtained >= 50 ? "D"
: "F"))))
?>

unusual ternary operation

I was asked to perform this operation of ternary operator use:
$test='one';
echo $test == 'one' ? 'one' : $test == 'two' ? 'two' : 'three';
Which prints two (checked using php).
I am still not sure about the logic for this. Please, can anybody tell me the logic for this.
Well, the ? and : have equal precedence, so PHP will parse left to right evaluating each bit in turn:
echo ($test == 'one' ? 'one' : $test == 'two') ? 'two' : 'three';
First $test == 'one' returns true, so the first parens have value 'one'. Now the second ternary is evaluated like this:
'one' /*returned by first ternary*/ ? 'two' : 'three'
'one' is true (a non-empty string), so 'two' is the final result.
Basically interpreter evaluates this expression from left to right, so:
echo $test == 'one' ? 'one' : $test == 'two' ? 'two' : 'three';
is interpreted as
echo ($test == 'one' ? 'one' : $test == 'two') ? 'two' : 'three';
And the expression in paratheses evaluates to true, since both 'one' and 'two' are not null/o/other form of false.
So if it would look like:
echo $test == 'one' ? FALSE : $test == 'two' ? 'two' : 'three';
It would print three. To make it work okay, you should forget about combining ternary operators, and use regular ifs/switch for more complicated logic, or at least use the brackets, for the interpreter to understand your logic, and not perform checking in standard LTR way:
echo $test == 'one' ? 'one' : ($test == 'two' ? 'two' : ($test == 'three' ? 'three' : 'four'));
//etc... It's not the most understandable code...
//You better use:
if($test == 'one')
echo 'one';
else { //or elseif()
...
}
//Or:
switch($test) {
case 'one':
echo 'one';
break;
case 'two':
echo 'two';
break;
//and so on...
}
It works correctly when you use brackets:
<?
$test='one';
echo $test == 'one' ? 'one' : ($test == 'two' ? 'two' : 'three');
I don't understand it 100% but without brackets, to the interpreter, the statement must look like this:
echo ($test == 'one' ? 'one' : $test == 'two') ? 'two' : 'three';
the result of the first condition seems to be returned as the result of the whole ternary operation.
I think that it is evaluated like this:
echo ($test == 'one' ? 'one' : $test == 'two') ? 'two' : 'three';
($test == 'one' ? 'one' : $test == 'two') is non-zero/null, so 'two' is logical output
if you want it to work correctly, write:
echo $test == 'one' ? 'one' : ($test == 'two' ? 'two' : 'three');
PHP'S documentation says:
Note: It is recommended that you avoid "stacking" ternary expressions. PHP's behaviour when using more than one ternary operator within a single statement is non-obvious:
Example #3 Non-obvious Ternary Behaviour
<?php
// on first glance, the following appears to output 'true'
echo (true?'true':false?'t':'f');
// however, the actual output of the above is 't'
// this is because ternary expressions are evaluated from left to right
// the following is a more obvious version of the same code as above
echo ((true ? 'true' : false) ? 't' : 'f');
// here, you can see that the first expression is evaluated to 'true', which
// in turn evaluates to (bool)true, thus returning the true branch of the
// second ternary expression.
?>
If you put parenthesis around the false statement, it prints one:
echo $test == 'one' ? 'one' : ($test == 'two' ? 'two' : 'three');
Ternary operators are executed in order of appearance so you really have:
echo ($test == 'one' ? 'one' : $test == 'two') ? 'two' : 'three';
Nested ternary operations are gross! The above explanation shows why.
Basically this is the logic:
is $test == 'one'
if TRUE then echo 'one'
else is $test == 'two'
if TRUE then echo 'two'
else echo three

Categories