Stacking Multiple Ternary Operators in PHP

39,926

Solution 1

Others have already suggested the right way of doing it but if you really want to use ternary operator you need to use parenthesis as:

$province = 7;
 $Myprovince = (
 ($province == 6) ? "city-1" :
  (($province == 7) ? "city-2" :
   (($province == 8) ? "city-3" :
    (($province == 30) ? "city-4" : "out of borders")))
 );

Updated Link

Solution 2

The ternary operator is evaluated from left to right. So if you don't group the expressions properly, you will get an unexpected result.

PHP's advice is [docs]:

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.

Your code actually is evaluated as:

(
    (
        (
            $province == 6 ? "city-1" : $province == 7
        ) ? "city-2" : 
        $province == 8
    ) ? "city-3" : $province == 30
) ? "city-4" : "out of borders";

where it should be

$province == 6 ? "city-1" : (
    $province == 7 ? "city-2" : (
        $province == 8 ? "city-3" : (
           $province == 30 ? "city-4" : "out of borders"
        )
    )
);

This code might look fine but someone will read it and they will need more time than they should to understand what this code is doing.


You would be better off with something like this:

$map = array( 6 = >'city-1', 
              7 => 'city-2', 
              8 => 'city-3', 
             30 => 'city-4');

$Myprovince = "out of borders";

if(array_key_exists($province, $map)) {
    $Myprovince = $map[$province];
}

Or as @Jonah mentioned in his comment:

$Myprovince = isset($map[$province]) ? $map[$province] : 'out of borders';

Solution 3

Don't abuse the ternary operator for that sort of thing. It makes debugging near impossible to follow. Why not do something like

switch($province) {
    case 6: $Myprovince = "city-1"; break;
    case 7: ...
}

or simply some chained if/then/else

if ($province == 6) {
     $Myprovince = "city-1";
} elseif ($province = ...) {
   ...
}

Solution 4

Some people have suggested using a switch statement or an if/else statement. But I would use an array instead, to make it easier to maintain and easier to read:

$provinces = array (
    6 => 'city-1',
    7 => 'city-2',
    8 => 'city-3',
    30 => 'city-4'
);

// then you can call:

$Myprovince = isset($provinces[$province]) ? $provinces[$province] : 'out of borders';

Why?

The code will probably eventually be easier to manage. Maybe you'll want to add those province-to-city mappings from database one day.. etc.. That will be hard to maintain with a bunch of switch/case statements.

Solution 5

I understand this is a question about PHP, but since this is just an educational exercise anyways I thought you might be interested in learning that Ruby and Javascript actually behave the way you expect.

Ruby:

ree-1.8.7-2012.02 :009 > def foo x
ree-1.8.7-2012.02 :010?>   x == 1 ? "city 1" : x == 2 ? "city 2" : "out of borders"
ree-1.8.7-2012.02 :011?>   end
 => nil
ree-1.8.7-2012.02 :012 > foo 1
 => "city 1"
ree-1.8.7-2012.02 :013 > foo 2
 => "city 2"
ree-1.8.7-2012.02 :014 > foo 3
 => "out of borders"

Javascript:

> function f(x) { return x == 1 ? "city 1" : x == 2 ? "city 2" : "out of borders"; }
undefined
> f(1)
"city 1"
> f(2)
"city 2"
> f(3)
"out of borders"
Share:
39,926

Related videos on Youtube

Mac Taylor
Author by

Mac Taylor

PHP Coder

Updated on January 13, 2022

Comments

  • Mac Taylor
    Mac Taylor over 2 years

    This is what I wrote :

     $Myprovince = (
    ($province == 6) ? "city-1" :
    ($province == 7) ? "city-2" :
    ($province == 8) ? "city-3" :
    ($province == 30) ? "city-4" : "out of borders"
    );
    

    But for every field I got the value city-4. I want to use ternary operators instead of switch/if because I want to experiment and see how it would be done.

    What's the problem with this code?

    • Jim
      Jim about 13 years
      Is there a reason you don't want to just use if / elseif or switch ?
    • Czechnology
      Czechnology about 13 years
      You'd need many more brackets for this to work. User a switch as Marc suggested.
    • Jonah
      Jonah about 13 years
      @Mac: you're driving me crazy with curiosity :D ! Why do you need to use ternary operators?
    • Mark Baker
      Mark Baker about 13 years
      Using ternary operators with complex nested conditions is not recommended for very good reasons... because they're fraught with problems, and it's extremely difficult to identify bugs. You've just discovered this! If you really knew how to use them, you wouldn't be asking for help! So why do you still want to use ternary operators in this case?
    • Mac Taylor
      Mac Taylor about 13 years
      im using ternary operators because of my own curiosity nothing else , i know its not good for complex conditions statement but in learning and testing php details i need to correct it :D
    • Jim
      Jim about 13 years
    • Jonah
      Jonah about 13 years
      Wow, this is the most heavily voted-on bunch of comments and answers I've ever seen.
    • Lightness Races in Orbit
      Lightness Races in Orbit about 13 years
      @Jonah: You must be new here.
    • Joe Phillips
      Joe Phillips about 13 years
      @Mac at least tell us you're not using this in real code so we can feel better about it
    • Mac Taylor
      Mac Taylor about 13 years
      god , please , I WOULD NOT USE THIS Method in a live script , never ever , are you better now ? just curious how to use it :D
    • Shanimal
      Shanimal about 12 years
      wow. If there's a legitimate reason the code is not working as expected, then by all means point it out. "answers" questioning the motives of arbitrary examples are far more inappropriate than the motive of arbitrary examples. +1 to codaddict and @arnorhs for thoughtful additions.
    • Curt
      Curt about 2 years
      It's very unfortunate that PHP got the precedence wrong for ?: when it's a common and useful construct in C that is EASY to read and use, and which every other language that borrowed it got right. I'm here because PHP 8 has been changed so it CRASHES without the unnecessary parens and BROKE my web site when I upgraded to it. For shame. Was my site already broken? NO, it was working fine -- and if they'd simply fixed the precedence they also would have fixed my inconsequential "bug".
  • Mac Taylor
    Mac Taylor about 13 years
    didn't i mentioned that i insist in using tenary operators because some reasons ?
  • Felix Kling
    Felix Kling about 13 years
    @Mac Taylor: No you didn't. What is the reason? Obscurity?
  • Marc B
    Marc B about 13 years
    @Felix: perhaps debugging masochism?
  • Jonah
    Jonah about 13 years
    Make it even shorter with this: $Myprovince = isset($map[$province]) ? $map[$province] : 'out of borders'; ;-)
  • Marc B
    Marc B about 13 years
    shudder I'd +1 your bravery for typing that out, but can't bring myself to "endorse" such a hideous thing...
  • Felix Kling
    Felix Kling about 13 years
    @Jonah: True :D I guess I wanted to go away from the ternary operator as far as possible ;)
  • Zeta Two
    Zeta Two about 13 years
    This is indeed the "correct" way to do it. I would never ever attempt anything like this though.
  • Mark Baker
    Mark Baker about 13 years
    Definitely warrants a +1 for persevering
  • Phoenix
    Phoenix about 13 years
    To your edit, I believe that the other lines are the c statements for the previous lines. Notice, no semi-colons. It's pretty much just: ($province == 6) ? "city-1" : ($province == 7) ? "city-2" : ($province == 8) ? "city-3" : ($province == 30) ? "city-4" : "out of borders";
  • John Parker
    John Parker about 13 years
    +1 Correct (yet horrifying). Still, you were just satisfying the OPs freakish whim to do the wrong thing. (Nothing to see here, move along please.) :-)
  • Mac Taylor
    Mac Taylor about 13 years
    thanks man this is what i looking for .. i don't even know why they trying to suggest other ways !
  • John Parker
    John Parker about 13 years
    @Mac If you really think this is the best solution (despite the disclaimer made by @codeaddict), then all I can hope is that I never have to maintain any of your code in the future. A switch statement or array lookup is the sane way to go if you care about code legibility/maintainability.
  • Jim
    Jim about 13 years
    They suggested other ways because you never said you wanted this done via ternary in your original post.
  • Felix Kling
    Felix Kling about 13 years
    @Phoenix: Yeah I noticed it and fixed it. I got very confused by all this ? and : ;)
  • Mac Taylor
    Mac Taylor about 13 years
    I said so . but a bad mistake happened , i created two topics mistakenly , and in that topic i said that "don't suggest other ways"... and lovely to see that , the topic is getting more and more negative points .
  • Felix Kling
    Felix Kling about 13 years
    @Mac Taylor: You are using the wrong tool for the job. There is nothing more to say about it. You could also create the string 'abc' with string concatenation 'a'.'b'.'c', but why doing so? Have fun adding a new city to your ternary beast and don't forget to count to match the parenthesis!
  • Felix Kling
    Felix Kling about 13 years
    @Mac Taylor: For more information, have a look at the documentation: php.net/manual/en/…
  • Lajos Mészáros
    Lajos Mészáros about 10 years
    PHP's the only language of the ternary operator capable languages, that works differently from other languages' implementation: phpsadness.com/sad/30
  • Hi-Angel
    Hi-Angel about 9 years
    I'm wonder, is there a documented reason for the author's code to not work? From what I know it looks to me like a PHP bug.
  • Thanh Trung
    Thanh Trung almost 9 years
    I would do like you. Definitely the best answer!
  • Steven Lu
    Steven Lu almost 9 years
    I think it is not a good response to reject the use of chained ternary as a programming pattern. It makes for concise clear code in the numerous languages that implement it "properly" and because it generates an expression, sometimes it is the only way to quickfix something. I used quotes because while the PHP designers did make the asinine choice to make its ternary operator left-associative, the fact is well-documented.
  • Emanuil Rusev
    Emanuil Rusev over 8 years
    The big benefit of the ternary over these is that it allows having a single variable assignment.
  • dreftymac
    dreftymac over 7 years
    shrug ... of course Ruby and Javascript already do it ... they are programming languages after all ^_^
  • Adam Copley
    Adam Copley almost 7 years
    definitely the best solution to this problem
  • Fandi Susanto
    Fandi Susanto over 4 years
    This one is good. Be careful of "out of memory" problem though.
  • Vladimir Ch
    Vladimir Ch over 3 years
    or $Myprovince = $map[$province] ?? 'out of borders';
  • mickmackusa
    mickmackusa about 3 years
    Switches are ugly, yes, but better would be a lookup array with a isset() check -- easy and clean.
  • Barmar
    Barmar over 2 years
    Note that PHP 8 has disabled nested ternaries without explicit parentheses.