在if语句中使用大量条件是否合适?

时间:2015-08-11 07:31:38

标签: php arrays if-statement

我有一个像这样的代码,我只想知道在单个if语句中使用大量'和'或'是好习惯

if (array_key_exists(self::SUB_FORM_CONTACT, $data) &&
            array_key_exists('company', $data[self::SUB_FORM_CONTACT]) &&
            ((array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company']) && !empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion'])) ||
                (array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company']) && !empty($data[self::SUB_FORM_CONTACT]['company'][''])))
        ) {

        }

或者有更好的方法吗?

4 个答案:

答案 0 :(得分:2)

PHP短路if语句。因此,如果您有多个if子句。

   if ($condition1 && $condition2 && $condition3 && $condition4)

然后,只要第一个表达式为假,PHP就会停止评估表达式。

要优化代码,请对表达式进行排序,以便首先评估最可能的失败代码。

参考文献: http://php.net/manual/en/language.operators.logical.php

Does PHP have short-circuit evaluation?

Is there a short-circuit OR in PHP that returns the left-most value?

答案 1 :(得分:2)

建议避免使用复杂的条件表达式,因为它们难以阅读和理解。但是,如果表达式仅使用一种类型的逻辑运算符来连接条件,则可以很容易地理解它。

例如:

if ($a && $b || $c && ! $d) {
    echo("Yes!");
}

乍一看,if分支(echo())中的代码何时执行?很可能没有。表达式包含所有逻辑运算的混合,如果不绘制$a$b$c$d的所有可能值的表格,则很难评估其最终值

另一方面,表达式:

if ($a && $b && $c && $d) {
    echo("Hooray!");
}

更容易掌握 当echo()$a$b$c同时$d时,TRUE语句会执行。

重构

在您的情况下,条件比上面提到的第一个更复杂。它包含所有逻辑运算符和括号,子表达式很长。

为了保持可读性,我会将复杂条件提取到(私有或受保护)方法中,该方法返回一个布尔值(谓词)。

private function isValidData(array $data)
{
    if (array_key_exists(self::SUB_FORM_CONTACT, $data) &&
        array_key_exists('company', $data[self::SUB_FORM_CONTACT]) &&
        ((array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company']) && !empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion'])) ||
            (array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company']) && !empty($data[self::SUB_FORM_CONTACT]['company'][''])))
    ) {
        return TRUE;
    } else {
        return FALSE;
    }
}

并替换if语句中的表达式:

if ($this->isValidData($data)) {
}

在接下来的步骤中,我会将复杂的条件表达式拆分为更小的条件,逐个测试它们,并在可能的情况下尽早使函数isValidData()返回(FALSE)。

第1步

因为表达式很复杂且片段相对较长,所以很难按原样使用它。这就是为什么,在第一步我将条件提取到具有较短名称的局部变量($a$b a.s.o.从上面):

private function isValidData(array $data)
{
    $a = array_key_exists(self::SUB_FORM_CONTACT, $data);
    $b = array_key_exists('company', $data[self::SUB_FORM_CONTACT]);
    $c = array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company']);
    $d = empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion']);
    $e = array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company']);
    $f = empty($data[self::SUB_FORM_CONTACT]['company']['']);
    // this should probably read 'serviceRegion' -------^

    if ($a && $b && (($c && ! $d) || ($e && ! $f))) {
        return TRUE;
    } else {
        return FALSE;
    }
}

现在一切看起来都很清楚,我们甚至发现了一个错误。太好了!

第2步

让我们尝试解开(现在更容易阅读)的条件:

if ($a && $b && (($c && ! $d) || ($e && ! $f))) {
    return TRUE;
} else {
    return FALSE;
}

如果我们一次进行一项测试并在结果明显时返回(TRUEFALSE),则可能会更容易理解。 该函数在TRUE$a时返回$b,并且包含表达式其余部分的括号在同一时间全部为TRUE

if语句可以这样重写:

if ($a) {
    if ($b) {
        if (($c && ! $d) || ($e && ! $f))) {
            return TRUE;
        }
    }
}

return FALSE;

通过颠倒$a$b上的条件,我们可以尽早返回FALSE

if (! $a) {
    return FALSE;
}
if (! $b) {
    return FALSE;
}

if (($c && ! $d) || ($e && ! $f))) {
    return TRUE;
}
return FALSE;

现在代码如下:

private function isValidData(array $data)
{
    $a = array_key_exists(self::SUB_FORM_CONTACT, $data);
    $form = $data[self::SUB_FORM_CONTACT];

    $b = array_key_exists('company', $form);

    $c = array_key_exists('salesRegion', $form['company']);
    $d = empty($form['company']['salesRegion']);

    $e = array_key_exists('serviceRegion', $form['company']);
    $f = empty($form['company']['serviceRegion']);


    if (! $a) {
        return FALSE;
    }
    if (! $b) {
        return FALSE;
    }

    if (($c && ! $d) || ($e && ! $f))) {
        return TRUE;
    }
}

请注意,因为从第二个开始的所有赋值都包含公共子表达式$data[self::SUB_FORM_CONTACT],所以我们将其提取到局部变量中。同样可以应用于最后4个分配(提取公共子表达式$form['company']

// ...
$b = array_key_exists('company', $form);
$company = $form['company'];

$c = array_key_exists('salesRegion', $company);
$d = empty($company['salesRegion']);

$e = array_key_exists('serviceRegion', $company);
$f = empty($company['serviceRegion']);

第3步

让我们注意到表达式$c && ! $d

array_key_exists('salesRegion', $company) && ! empty($company['salesRegion'])

实际上并不需要调用array_key_exists()。我想其目的是避免在访问$company['salesRegion']$company不包含密钥'salesRegion'时触发通知。当数组中不存在该键并且它不会触发通知时,empty() PHP构造将很乐意返回TRUE

$e && ! $f也是如此。这样我们就可以完全删除$c$e。条件如下:

if (! $d || ! $f) {
    return TRUE;
}

或者

if (! ($d && $f)) {
    return TRUE;
}

否定条件:

if ($d && $f) {
    return FALSE;
} else {
    return TRUE;
}

完整的功能现在看起来像这样:

private function isValidData(array $data)
{
    $a = array_key_exists(self::SUB_FORM_CONTACT, $data);
    $form = $data[self::SUB_FORM_CONTACT];

    $b = array_key_exists('company', $form);
    $company = $form['company'];

    $d = empty($company['salesRegion']);
    $f = empty($company['serviceRegion']);


    if (! $a) {
        return FALSE;
    }
    if (! $b) {
        return FALSE;
    }

    if ($d && $f) {
        return FALSE;
    } else {
        return TRUE;
    }
}

最后一步

我们现在可以删除局部变量$a$b$d$f,然后移动$form$company的初始化我们知道它们不是NULL(每个人在对array_key_exists()的相应调用之后):

该功能的最终形式是:

/**
 * Verify if the provided data is valid.
 *
 * @param array $data the data to validate
 * @return boolean TRUE if the data is valid, FALSE if some field is missing or empty
 */
private function isValidData(array $data)
{
    // $data must contain the contact form information
    if (! (array_key_exists(self::SUB_FORM_CONTACT, $data))) {
        return FALSE;
    }

    // The contact form information exists in $data
    $form = $data[self::SUB_FORM_CONTACT];
    // The form must contain company information
    if (! (array_key_exists('company', $form))) {
        return FALSE;
    }

    // The company information exists in the contact form
    $company = $form['company'];
    // The company must contains information about salesRegion or serviceRegion or both
    if (empty($company['salesRegion']) && empty($company['serviceRegion'])) {
        return FALSE;
    }

    // The data is valid
    return TRUE;
}

结论

代码比以前长了几行,但是:

  • 这些条件更容易阅读和理解,因为它们是一个接一个地评估而不是一个复杂的大表达;
  • 代码封装在一个函数中; if ($this->isValidData($data))条件的读者现在一眼就能理解会发生什么,而不需要阅读和理解它是如何发生的;如果有效则不要担心;
  • 验证代码可以独立于数据有效时的情况进行测试;
  • 如果需要且适当,可以通过使用不同的数据调用方法(可能通过不同的表单提交)来重用代码。

答案 2 :(得分:0)

就可读性而言,您应该正确格式化代码,一切都应该没问题(给定示例中的格式化正常)。在调试方面,在定义哪个语句失败时可能存在一些问题,但仍然 - 使用正确的格式化,您可以只注释掉几行easyli并检查结果。在表现的情况下,我认为它甚至更好。我的意思是你无论如何都必须以某种方式检查所有这些条件。如果你把它们全部放在不同的ifs中,它们会一个接一个地被检查(好的,你可以写它以便不是所有的都被检查,但我认为它只是不必要地延长你的代码)。如果您正确撰写陈述,则不必检查所有陈述。例如,如果第一个条件为假,则有大量&&,甚至不检查下一个条件。你应该在开始时放置更少的可能性,更轻量级(在性能方面)条件,并在最后留下沉重的,可能的。如果你想保持你的主要代码干净,你也可以把它包装在一个函数中。

答案 3 :(得分:0)

这是一种糟糕的性能解决方案。但是,一般来说,我只是在处理表单时并不关心它,如果它是地球上最有效的东西 - 它不是内核代码。我刚刚解决了一个类似的问题,就是抛出192演出的ram .....在此基础上它可以节省数百到数千个开发人员的时间,这比ram要贵很多。

$hasdata            = array_key_exists(self::SUB_FORM_CONTACT, $data);
$hasCompany         = array_key_exists('company', $data[self::SUB_FORM_CONTACT]);
$hasSalesRegion     = array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company']) 
$hasSalesRegion     = $hasSalesRegion && !empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion']);
$hasServiceRegion   = array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company']) 
$hasServiceRegion   = $hasServiceRegion && !empty($data[self::SUB_FORM_CONTACT]['company']);

$evalResult = $hasdata && $hasCompany && ($hasSalesRegion || $hasServiceRegion);

if($evalResult)
{
    // do your thing
}

$hasdata = array_key_exists(self::SUB_FORM_CONTACT, $data); $hasCompany = array_key_exists('company', $data[self::SUB_FORM_CONTACT]); $hasSalesRegion = array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company']) $hasSalesRegion = $hasSalesRegion && !empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion']); $hasServiceRegion = array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company']) $hasServiceRegion = $hasServiceRegion && !empty($data[self::SUB_FORM_CONTACT]['company']); $evalResult = $hasdata && $hasCompany && ($hasSalesRegion || $hasServiceRegion); if($evalResult) { // do your thing }

我更希望看到这样的东西,而不是获得短切割的好处,并且难以阅读双重否定。