switch 语句中的奇怪行为

Posted

技术标签:

【中文标题】switch 语句中的奇怪行为【英文标题】:Odd behaviour in a switch statement 【发布时间】:2011-05-05 03:05:38 【问题描述】:

我正在为可排序表编写代码,其中单击标题中的链接会更改生成一组搜索结果时执行的 ORDER BY(没有有效 order by 的情况会导致查询无法运行使用 order by 并按照数据库返回的顺序返回结果。这是设计的)。该代码是在我的雇主提供的框架内编写的。

为了验证查询的 ORDER BY 部分,我通过以下验证函数运行输入。

<?php
function sortMode ($name)

    $mode   = '';
    switch ($name)
    
        case 'resnum'   : $mode = 'b_resnum';            break;
        case 'state'    : $mode = 'st_id';               break;
        case 'name'     : $mode = 'lastname, firstname'; break;
        case 'phone'    : $mode = 'phone';               break;
        case 'email'    : $mode = 'email';               break;
        case 'opened'   : $mode = 'cs_created';          break;
        default         : $mode = '';                    break;
    
    return ($mode);

?>

在测试中我发现如果没有提供参数,那么排序顺序将是 resnum。经过一些实验,我发现框架中内置的过滤会导致对未初始化变量(例如未设置的 GET 参数)的请求返回整数 0。如果上面的代码得到一个 0 整数作为其输入,它将始终遵循第一个可用的执行路径。

作为一个实验,我尝试在 switch 语句中重新排列 case 的顺序,发现如果这个函数被传递一个 0,那么顶部的内容就是执行的内容。

问题的解决方案是使用switch (strval($name)),因此特定问题得到了解决,但现在我对 PHP switch 语句的一般行为感到好奇。我目睹的行为是 PHP 的正确行为吗? PHP 中是否存在导致此问题的错误,或者我在代码中犯了一个我不知道的错误?

【问题讨论】:

【参考方案1】:

这是因为 php 将字符串转换为整数的方式。当您传入0 时,您要求它进行整数比较,因此它会将您的所有大小写键转换为整数。当 php 将 string 转换为 int 时,它会在字符串的开头查找一个实际数字,然后将这个数字吞噬,直到找到一个非数字。由于字符串“resnum”没有数字,所以它返回 0。见这里:

php > echo (int)"100";
100
php > echo (int)"300 dogs";
300
php > echo (int)"resnum";
0
php > echo (int)"resnum 100";
0

由于所有这些字符串都被强制转换为 0,因此第一种情况的计算结果为 true,因为 0 == 0

资源:String conversion to numbersType comparison tables


挑剔时间。当您执行将字符串映射到字符串的简单 case 语句时,请使用数组。它更清晰,实际上更快:

function sortMode ($name)

    $modeMap = array(
        'resnum'   => 'b_resnum',
        'state'    => 'st_id',
        'name'     => 'lastname, firstname',
        'phone'    => 'phone',
        'email'    => 'email',
        'opened'   => 'cs_created'
    );

    return isset($modeMap[$name]) ? $modeMap[$name] : '';

如果在映射中设置了$name,我们返回键映射到的值。否则,我们返回一个空字符串,它代替了default 的情况。

作为奖励,如果您使用上述方法,您会更早注意到该错误,因为它会尝试访问 $modeMap[0] 并且会返回您的默认情况。

【讨论】:

PHP 可能是纯粹的邪恶。这种自动 int 转换比“节省”的时间更令人沮丧。 至少在 if 中可以使用 === 并防止这种情况发生,但不幸的是开关/案例是 == 欢迎来到 PHP 世界,先生们。 typecast 也是switch((typecast)$var) 的一种方式,我更喜欢array_key_exists @maggie array_key_exists 仅在您有任何 null 的键时才需要。否则,使用isset 会更好,因为它是一种语言结构,没有array_key_exists 那样的函数调用开销。【参考方案2】:

关键是 switch() 语句执行参数和标签之间的比较。这意味着您必须处理 PHP 比较和类型转换规则。看看一些例子:

<?php

var_dump( ''==0 );    // bool(true)
var_dump( 'foo'==0 ); // bool(true)
var_dump( '0'==0 );   // bool(true)
var_dump( '0m'==0 );  // bool(true)
var_dump( '01'==0 );  // bool(false)

参考可以在以下位置找到:

http://es.php.net/manual/en/language.operators.comparison.php http://es.php.net/manual/en/types.comparisons.php#types.comparisions-loose http://es.php.net/manual/en/control-structures.switch.php

将未设置的 GET 参数初始化为 0 是一个非常奇怪的设计决定。您应该以单独的方式处理该特殊情况,以明确这是一种特殊情况:

if( $name===0 )
    return '';

switch($name)
    // ...

【讨论】:

以上是关于switch 语句中的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Switch 需要语句但接受表达式

切换与var / null奇怪的行为

C++ 中的 if 语句行为真的很奇怪

if语句中的奇怪行为

while循环中的高级switch语句?

在switch中的case语句中声明变量会被提前