📜 ⬆️ ⬇️

The order of the ternary operator

Php has an interesting feature of the ternary operator - a specific and unique order of execution.

$ python -c "print 1 if true else 2 if true else 3 if true else 4 if true else 5 "
1

$ node -e " true ? 1 : true ? 2 : true ? 3 : true ? 4 : 5 "
1

$ perl -e "print true ? 1 : true ? 2 : true ? 3 : true ? 4 : 5 "
1

$ ruby -e "print true ? 1 : true ? 2 : true ? 3 : true ? 4 : 5 "
1

$ php -r "print true ? 1 : true ? 2 : true ? 3 : true ? 4 : 5 ;"
4
Java and C ++ will also return 1


And what's the difference?


I know about this interesting nuance for a long time, but just yesterday I discovered an error in one of the open source: the author clearly did not know about this nuance and was caught. Therefore, this article is just a warning. After all, if a programmer expects the same behavior from php as from other languages, he may end up in a haleppe.
')
This technique is very convenient for setting values, depending on the conditions. Elegant replacement if-else. For example:
 value = isCondFirst() ? valueFirst() : isCondSecond() ? valueSecond() : isCondThird() ? valueThird() : valueDefault(); /**********  **********/ if (isCondFirst()) { value = valueFirst(); } else if (isCondSecond()) { value = valueSecond(); } else if (isCondThird()) { value = valueThird(); } else { value = valueDefault(); } 


How to avoid mistakes?


The first way is not to use the ternary operator in php.
The second is to directly indicate the order of execution using parentheses:

$ php -r "print true ? 1 : ( true ? 2 : ( true ? 3 : ( true ? 4 : 5 )));"
1

Something like Lisp, isn’t it?

Why does it even happen this way?


Let's understand the order of execution of the ternary operator on the example of JavaScript vs PHP
Let's write two test scripts to understand how each of the languages ​​works.

Just in case, I will explain that with $foo = $lambda('fooMsg', 'fooReturn') , $foo contains a function that, when called, will output the message 'fooMsg' to the console and return the value of 'fooReturn'

$ cat ternary. js
 var lambda = function (logMsg, returnValue) { return function () { console.log(logMsg); return returnValue; }; }; var cond = { first : lambda('cond.first' , true), second: lambda('cond.second', true), third : lambda('cond.third' , true) }; var value = { first : lambda('value.first' , 'first'), second : lambda('value.second' , 'second'), third : lambda('value.third' , 'third'), default: lambda('value.default', 'default') }; console.log( 'result: ', cond.first() ? value.first() : cond.second() ? value.second() : cond.third() ? value.third() : value.default() ); 


$ node ternary. js
cond.first
value.first
result: first


$ cat ternary. php
 <?php $lambda = function ($logMsg, $returnValue) { return function () use ($logMsg, $returnValue) { echo $logMsg . PHP_EOL; return $returnValue; }; }; $cond = array( 'first' => $lambda('cond.first' , true), 'second'=> $lambda('cond.second', true), 'third' => $lambda('cond.third' , true), ); $value = array( 'first' => $lambda('value.first' , 'first'), 'second' => $lambda('value.second' , 'second'), 'third' => $lambda('value.third' , 'third'), 'default'=> $lambda('value.default', 'default'), ); echo 'result: ' . ( $cond['first']() ? $value['first']() : $cond['second']() ? $value['second']() : $cond['third']() ? $value['third']() : $value['default']() ) . PHP_EOL; ?> 

$ php ternary. php
cond.first
value.first
value.second
value.third
result: third


Which of this result can be concluded. Javascript parses the ternary operator is quite logical. First checks the leftmost condition. If it is true, then execute and return the left part after the first colon, if not true, then the right part.
 (cond.first() ? value.first() : (cond.second() ? value.second() : (cond.third() ? value.third() : (value.default())))); /******** * ===> */ true ? 'value.first' : /* ignored */; 


PHP thinks original.

 ( ( cond.first() ? value.first() : cond.second() ) ? value.second() : cond.third() ) ? value.third() : value.default(); /******** * ===> */ ( ( 'value.first' ) ? value.second() : cond.third() ) ? value.third() : value.default(); /******** * ===> */ ( 'value.second' ) ? value.third() : value.default(); /******** * ===> */ 'value.third' 


As confirmed in the official manual :
 $a = true ? 0 : true ? 1 : 2; // (true ? 0 : true) ? 1 : 2 = 2 


Conclusion


In general, PHP itself showed itself again "at its best", but the problem is far from being critical. The main thing is to know about it and be careful with the ternary operator in PHP.

Source: https://habr.com/ru/post/114899/


All Articles