Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

strange behaviour ternary operator [closed]

Tags:

php

Coming from C# I must do a project in PHP.

I am using this code:

$transport = 'T';

$vehicle = (
 ( $transport == 'B' ) ? 'bus' :
 ( $transport == 'A' ) ? 'airplane' :
 ( $transport == 'T' ) ? 'train' :
 ( $transport == 'C' ) ? 'car' :
 ( $transport == 'H' ) ? 'horse' :
 'feet' );

echo $vehicle;

I would expect it to print train, but I get horse. Codepad example: http://codepad.org/rWllfrht

Who can explain this strange behaviour?

like image 795
Piepje Avatar asked Nov 29 '25 06:11

Piepje


2 Answers

Not seeing any explanation about why your code is broken in the other answers, so here is a quick run-down.

The problem here is made more obvious is you add brackets to make the implicit order of evaluation more explicit.

Here's a trimmed down version of your code, which still produces the incorrect result of "horse":

 $t = 'T';

 ( $t == 'T' ) ? 'train' : 
 ( $t == 'C' ) ? 'car' : 
 ( $t == 'H' ) ? 'horse' : 'feet';

First, lets unroll it:

( $t == 'T' ) ? 'train' : ( $t == 'C' ) ? 'car' : ( $t == 'H' ) ? 'horse' : 'feet'; 

Next, I'll add explicit parenthesis where there are already implicit ones:

((($t == 'T') ? 'train' : ($t == 'C')) ? 'car' : ($t == 'H')) ? 'horse' : 'feet';

Next, we can resolve your comparisons:

((true ? 'train' : false) ? 'car' : false) ? 'horse' : 'feet';

You should start to see why this is broken. The first ternary evaluates true ? 'train' : 'false' to 'train':

('train' ? 'car' : false) ? 'horse' : 'feet';

Because 'train' is true when cast to a boolean, the result is now 'car':

'car' ? 'horse' : 'feet';

Again, because a non-empty string is "true", the result is now 'horse'. So, the first time a true comes up in your horrible nested case statement, the result will cascade through all remaining statements, throwing out the previous value for the "true" branch of the next operator.

The solution is to avoid this code. It is an attempt to be far, far too clever, and the result is a broken, unreadable mess. There is absolutely no reason to use it. Choose a switch statement, it's purpose built for exactly what you're trying to do.

like image 191
meagar Avatar answered Nov 30 '25 19:11

meagar


This doesn't work as expected due to a bug in the PHP language grammar, as seen at: http://en.wikipedia.org/wiki/%3F:#PHP

Here's a simple version that DOES work:

$transport = 'T';

$vehicle = (
 ( $transport == 'B' ? 'bus' :
 ( $transport == 'A' ? 'airplane' :
 ( $transport == 'T'  ? 'train' :
 ( $transport == 'C'  ? 'car' :
 ( $transport == 'H'  ? 'horse' :
 'feet' ))))));

echo $vehicle;

But as everyone else said, I agree this isn't the best way to do this. You could use a switch case, if else if, or associative array and be a lot more readable.

like image 39
Chris Miller Avatar answered Nov 30 '25 18:11

Chris Miller