关于php:奇怪的行为三元运算符


strange behaviour ternary operator

来自C我必须用PHP做一个项目。

我正在使用此代码:

1
2
3
4
5
6
7
8
9
10
11
$transport = 'T';

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

echo $vehicle;

我希望它能打印train,但我得到horse。代码板示例:http://codepad.org/rwllfrht

谁能解释这种奇怪的行为?


在其他答案中没有看到任何关于代码为什么被破坏的解释,所以这里有一个快速的运行。

这里的问题更为明显,您添加了括号以使隐式计算顺序更显式。

下面是您的代码的精简版本,它仍然会产生不正确的"horse"结果:

1
2
3
4
5
 $t = 'T';

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

首先,让我们展开它:

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

接下来,我将在已经有隐式括号的地方添加显式括号:

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

接下来,我们可以解决您的比较问题:

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

你应该开始明白为什么这个坏了。第一个三元对true ? 'train' : 'false''train'进行评价:

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

因为当强制转换为布尔值时,'train'为真,所以结果现在为'car'

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

同样,因为非空字符串是"true",所以结果现在是"horse"。所以,第一次在可怕的嵌套case语句中出现true时,结果将级联到所有剩余的语句中,抛出下一个运算符的"true"分支的前一个值。

解决方案是避免使用此代码。这是一种试图走远,太聪明的尝试,结果是一个破碎的,不可读的混乱。完全没有理由使用它。选择一个switch语句,它的目的就是为您想要做的事情而构建。


由于在php语言语法中出现了一个bug,这无法按预期工作,如:http://en.wikipedia.org/wiki/%3f:php所示。

这里有一个简单的版本,可以工作:

1
2
3
4
5
6
7
8
9
10
11
$transport = 'T';

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

echo $vehicle;

但正如其他人所说,我同意这不是最好的方法。您可以使用开关盒,如果不是,或者关联数组,并且可读性更高。


这是一种"按预期工作,即使它明显是错误的"PHP行为。它不会以这种方式关联,所以尽管这段代码可以在大多数其他语言中工作,但它在PHP中会失败。教训?学会在不寻常的关联范例上使用括号。第二课?三元不是一个神奇的子弹,虽然它可以很好和紧凑,它应该只在它可读的时候使用。imho嵌套的三元语句很难看。


如果你想这样做,就要学会爱妄想症:

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

由于php对三元的操作顺序http://php.net/manual/en/language.operators.comparison.php,三元的每个右侧都需要清楚地包含。

顺便说一下,从那个页面,他们明确建议不要像这样堆叠它们…

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:


我不清楚您为什么选择使用这种形式的语法,正如在注释中提到的,这将是调试的一个噩梦…换个箱子可能是更好的选择-

1
2
3
4
5
6
7
8
9
10
11
12
13
$vehicle = '';
switch($transport){
  case 'B' :
    $vehicle = 'bus';
  break;
  case 'A' :
    $vehicle = 'airplane';
  break;
  ...
  default:
   // undefined cases
  break;
}

参考文献-

  • switch statement


我不建议你使用这样的代码,但为了教育目的,它应该是

1
2
3
4
5
6
7
8
9
10
$transport = 'T';
$vehicle = (
        ($transport == 'B') ? 'bus' :
            (($transport == 'A') ? 'airplane' :
                (($transport == 'T') ? 'train' :
                        (($transport == 'C') ? 'car' :
                                (($transport == 'H') ? 'horse' : 'feet'))))
        );

echo $vehicle;

更好的代码应该是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$transport = 'T';
switch ($transport) {
    case 'A' :
        $vehicle = 'airplane';
        break;
    case 'B' :
        $vehicle = 'bus';
        break;
    case 'C' :
        $vehicle = 'car';
        break;
    case 'H' :
        $vehicle = 'horse';
        break;
    case 'T' :
        $vehicle = 'train';
        break;
    default :
        $vehicle = 'teleportation';
        break;
}

echo $vehicle;

或者更好:

1
2
3
$transport = 'T';
$array = array('A'=>'airplane','B'=>"bus","C"=>"car","H"=>"horse","T"=>"train");
echo isset($array[$transport]) ? $array[$transport] : null;

或者,使用数据库:

1
 SELECT name FROM transpotationTable WHERE someKey = '$transport'